Mercurial > hg > orthanc
changeset 5718:77e1ff7f90c7 find-refactoring-clean
integration find-refactoring->find-refactoring-clean
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Sat, 13 Jul 2024 00:59:01 +0200 |
parents | 593e110de3d8 (diff) c5ca97d21023 (current diff) |
children | f488fc5c972a |
files | |
diffstat | 6 files changed, 94 insertions(+), 1857 deletions(-) [+] |
line wrap: on
line diff
--- a/OrthancServer/Sources/OrthancFindRequestHandler.cpp Sat Jul 13 00:58:29 2024 +0200 +++ b/OrthancServer/Sources/OrthancFindRequestHandler.cpp Sat Jul 13 00:59:01 2024 +0200 @@ -82,102 +82,6 @@ } - static void AddAnswer(DicomFindAnswers& answers, - ServerContext& context, - const std::string& publicId, - const std::string& instanceId, - const DicomMap& mainDicomTags, - const Json::Value* dicomAsJson, - ResourceType level, - const DicomArray& query, - const std::list<DicomTag>& sequencesToReturn, - const std::string& defaultPrivateCreator, - const std::map<uint16_t, std::string>& privateCreators, - const std::string& retrieveAet, - bool allowStorageAccess) - { - ExpandedResource resource; - std::set<DicomTag> requestedTags; - - query.GetTags(requestedTags); - requestedTags.erase(DICOM_TAG_QUERY_RETRIEVE_LEVEL); // this is not part of the answer - - // reuse ExpandResource to get missing tags and computed tags (ModalitiesInStudy ...). This code is therefore shared between C-Find, tools/find, list-resources and QIDO-RS - context.ExpandResource(resource, publicId, mainDicomTags, instanceId, dicomAsJson, - level, requestedTags, ExpandResourceFlags_IncludeMainDicomTags, allowStorageAccess); - - DicomMap result; - - /** - * Add the mandatory "Retrieve AE Title (0008,0054)" tag, which was missing in Orthanc <= 1.7.2. - * http://dicom.nema.org/medical/dicom/current/output/html/part04.html#sect_C.4.1.1.3.2 - * https://groups.google.com/g/orthanc-users/c/-7zNTKR_PMU/m/kfjwzEVNAgAJ - **/ - result.SetValue(DICOM_TAG_RETRIEVE_AE_TITLE, retrieveAet, false /* not binary */); - - for (size_t i = 0; i < query.GetSize(); i++) - { - if (query.GetElement(i).GetTag() == DICOM_TAG_QUERY_RETRIEVE_LEVEL) - { - // Fix issue 30 on Google Code (QR response missing "Query/Retrieve Level" (008,0052)) - result.SetValue(query.GetElement(i).GetTag(), query.GetElement(i).GetValue()); - } - else if (query.GetElement(i).GetTag() == DICOM_TAG_SPECIFIC_CHARACTER_SET) - { - // Do not include the encoding, this is handled by class ParsedDicomFile - } - else - { - const DicomTag& tag = query.GetElement(i).GetTag(); - const DicomValue* value = resource.GetMainDicomTags().TestAndGetValue(tag); - - if (value != NULL && - !value->IsNull() && - !value->IsBinary()) - { - result.SetValue(tag, value->GetContent(), false); - } - else - { - result.SetValue(tag, "", false); - } - } - } - - if (result.GetSize() == 0 && - sequencesToReturn.empty()) - { - CLOG(WARNING, DICOM) << "The C-FIND request does not return any DICOM tag"; - } - else if (sequencesToReturn.empty()) - { - answers.Add(result); - } - else if (dicomAsJson == NULL) - { - CLOG(WARNING, DICOM) << "C-FIND query requesting a sequence, but reading JSON from disk is disabled"; - answers.Add(result); - } - else - { - ParsedDicomFile dicom(result, GetDefaultDicomEncoding(), - true /* be permissive, cf. issue #136 */, defaultPrivateCreator, privateCreators); - - for (std::list<DicomTag>::const_iterator tag = sequencesToReturn.begin(); - tag != sequencesToReturn.end(); ++tag) - { - assert(dicomAsJson != NULL); - const Json::Value& source = (*dicomAsJson) [tag->Format()]; - - CopySequence(dicom, *tag, source, defaultPrivateCreator, privateCreators); - } - - answers.Add(dicom); - } - } - - - bool OrthancFindRequestHandler::FilterQueryTag(std::string& value /* can be modified */, ResourceType level, const DicomTag& tag, @@ -248,88 +152,6 @@ } - class OrthancFindRequestHandler::LookupVisitor : public ServerContext::ILookupVisitor - { - private: - DicomFindAnswers& answers_; - ServerContext& context_; - ResourceType level_; - const DicomMap& query_; - DicomArray queryAsArray_; - const std::list<DicomTag>& sequencesToReturn_; - std::string defaultPrivateCreator_; // the private creator to use if the group is not defined in the query itself - const std::map<uint16_t, std::string>& privateCreators_; // the private creators defined in the query itself - std::string retrieveAet_; - FindStorageAccessMode findStorageAccessMode_; - - public: - LookupVisitor(DicomFindAnswers& answers, - ServerContext& context, - ResourceType level, - const DicomMap& query, - const std::list<DicomTag>& sequencesToReturn, - const std::map<uint16_t, std::string>& privateCreators, - FindStorageAccessMode findStorageAccessMode) : - answers_(answers), - context_(context), - level_(level), - query_(query), - queryAsArray_(query), - sequencesToReturn_(sequencesToReturn), - privateCreators_(privateCreators), - findStorageAccessMode_(findStorageAccessMode) - { - answers_.SetComplete(false); - - { - OrthancConfiguration::ReaderLock lock; - defaultPrivateCreator_ = lock.GetConfiguration().GetDefaultPrivateCreator(); - retrieveAet_ = lock.GetConfiguration().GetOrthancAET(); - } - } - - virtual bool IsDicomAsJsonNeeded() const ORTHANC_OVERRIDE - { - // Ask the "DICOM-as-JSON" attachment only if sequences are to - // be returned OR if "query_" contains non-main DICOM tags! - - DicomMap withoutSpecialTags; - withoutSpecialTags.Assign(query_); - - // Check out "ComputeCounters()" - withoutSpecialTags.Remove(DICOM_TAG_MODALITIES_IN_STUDY); - withoutSpecialTags.Remove(DICOM_TAG_NUMBER_OF_PATIENT_RELATED_INSTANCES); - withoutSpecialTags.Remove(DICOM_TAG_NUMBER_OF_PATIENT_RELATED_SERIES); - withoutSpecialTags.Remove(DICOM_TAG_NUMBER_OF_PATIENT_RELATED_STUDIES); - withoutSpecialTags.Remove(DICOM_TAG_NUMBER_OF_SERIES_RELATED_INSTANCES); - withoutSpecialTags.Remove(DICOM_TAG_NUMBER_OF_STUDY_RELATED_INSTANCES); - withoutSpecialTags.Remove(DICOM_TAG_NUMBER_OF_STUDY_RELATED_SERIES); - withoutSpecialTags.Remove(DICOM_TAG_SOP_CLASSES_IN_STUDY); - - // Check out "AddAnswer()" - withoutSpecialTags.Remove(DICOM_TAG_SPECIFIC_CHARACTER_SET); - withoutSpecialTags.Remove(DICOM_TAG_QUERY_RETRIEVE_LEVEL); - - return (!sequencesToReturn_.empty() || - !withoutSpecialTags.HasOnlyMainDicomTags()); - } - - virtual void MarkAsComplete() ORTHANC_OVERRIDE - { - answers_.SetComplete(true); - } - - virtual void Visit(const std::string& publicId, - const std::string& instanceId, - const DicomMap& mainDicomTags, - const Json::Value* dicomAsJson) ORTHANC_OVERRIDE - { - AddAnswer(answers_, context_, publicId, instanceId, mainDicomTags, dicomAsJson, level_, queryAsArray_, sequencesToReturn_, - defaultPrivateCreator_, privateCreators_, retrieveAet_, IsStorageAccessAllowedForAnswers(findStorageAccessMode_)); - } - }; - - namespace { class LookupVisitorV2 : public ResourceFinder::IVisitor @@ -622,31 +444,12 @@ * Run the query. **/ - size_t limit = (level == ResourceType_Instance) ? maxInstances_ : maxResults_; - - - if (true) - { - /** - * EXPERIMENTAL VERSION - **/ - - ResourceFinder finder(level, false /* don't expand */); - finder.SetDatabaseLookup(lookup); - finder.AddRequestedTags(requestedTags); + ResourceFinder finder(level, false /* don't expand */); + finder.SetDatabaseLookup(lookup); + finder.AddRequestedTags(requestedTags); - LookupVisitorV2 visitor(answers, *filteredInput, sequencesToReturn, privateCreators); - finder.Execute(visitor, context_); - } - else - { - /** - * VERSION IN ORTHANC <= 1.12.4 - **/ - - LookupVisitor visitor(answers, context_, level, *filteredInput, sequencesToReturn, privateCreators, context_.GetFindStorageAccessMode()); - context_.Apply(visitor, lookup, level, 0 /* "since" is not relevant to C-FIND */, limit); - } + LookupVisitorV2 visitor(answers, *filteredInput, sequencesToReturn, privateCreators); + finder.Execute(visitor, context_); }
--- a/OrthancServer/Sources/OrthancRestApi/OrthancRestResources.cpp Sat Jul 13 00:58:29 2024 +0200 +++ b/OrthancServer/Sources/OrthancRestApi/OrthancRestResources.cpp Sat Jul 13 00:59:01 2024 +0200 @@ -146,77 +146,6 @@ // List all the patients, studies, series or instances ---------------------- - static void AnswerListOfResources1(RestApiOutput& output, - ServerContext& context, - const std::list<std::string>& resources, - const std::map<std::string, std::string>& instancesIds, // optional: the id of an instance for each found resource. - const std::map<std::string, boost::shared_ptr<DicomMap> >& resourcesMainDicomTags, // optional: all tags read from DB for a resource (current level and upper levels) - const std::map<std::string, boost::shared_ptr<Json::Value> >& resourcesDicomAsJson, // optional: the dicom-as-json for each resource - ResourceType level, - bool expand, - DicomToJsonFormat format, - const std::set<DicomTag>& requestedTags, - bool allowStorageAccess) - { - Json::Value answer = Json::arrayValue; - - for (std::list<std::string>::const_iterator - resource = resources.begin(); resource != resources.end(); ++resource) - { - if (expand) - { - Json::Value expanded; - - std::map<std::string, std::string>::const_iterator instanceId = instancesIds.find(*resource); - if (instanceId != instancesIds.end()) // if it is found in instancesIds, it is also in resourcesDicomAsJson and mainDicomTags - { - // reuse data already collected before (e.g during lookup) - std::map<std::string, boost::shared_ptr<DicomMap> >::const_iterator mainDicomTags = resourcesMainDicomTags.find(*resource); - std::map<std::string, boost::shared_ptr<Json::Value> >::const_iterator dicomAsJson = resourcesDicomAsJson.find(*resource); - - context.ExpandResource(expanded, *resource, - *(mainDicomTags->second.get()), - instanceId->second, - dicomAsJson->second.get(), - level, format, requestedTags, allowStorageAccess); - } - else - { - context.ExpandResource(expanded, *resource, level, format, requestedTags, allowStorageAccess); - } - - if (expanded.type() == Json::objectValue) - { - answer.append(expanded); - } - } - else - { - answer.append(*resource); - } - } - - output.AnswerJson(answer); - } - - - static void AnswerListOfResources2(RestApiOutput& output, - ServerContext& context, - const std::list<std::string>& resources, - ResourceType level, - bool expand, - DicomToJsonFormat format, - const std::set<DicomTag>& requestedTags, - bool allowStorageAccess) - { - std::map<std::string, std::string> unusedInstancesIds; - std::map<std::string, boost::shared_ptr<DicomMap> > unusedResourcesMainDicomTags; - std::map<std::string, boost::shared_ptr<Json::Value> > unusedResourcesDicomAsJson; - - AnswerListOfResources1(output, context, resources, unusedInstancesIds, unusedResourcesMainDicomTags, unusedResourcesDicomAsJson, level, expand, format, requestedTags, allowStorageAccess); - } - - template <enum ResourceType resourceType> static void ListResources(RestApiGetCall& call) { @@ -239,96 +168,45 @@ .SetHttpGetSample("https://orthanc.uclouvain.be/demo/" + resources + "?since=0&limit=2", true); return; } - - ServerIndex& index = OrthancRestApi::GetIndex(call); - ServerContext& context = OrthancRestApi::GetContext(call); - - if (true) + + // TODO-FIND: include the FindRequest options parsing in a method (parse from get-arguments and from post payload) + // TODO-FIND: support other values for expand like expand=MainDicomTags,Labels,Parent,SeriesStatus + const bool expand = (call.HasArgument("expand") && + call.GetBooleanArgument("expand", true)); + + std::set<DicomTag> requestedTags; + OrthancRestApi::GetRequestedTags(requestedTags, call); + + ResourceFinder finder(resourceType, expand); + finder.AddRequestedTags(requestedTags); + + if (call.HasArgument("limit") || + call.HasArgument("since")) { - /** - * EXPERIMENTAL VERSION - **/ - - // TODO-FIND: include the FindRequest options parsing in a method (parse from get-arguments and from post payload) - // TODO-FIND: support other values for expand like expand=MainDicomTags,Labels,Parent,SeriesStatus - const bool expand = (call.HasArgument("expand") && - call.GetBooleanArgument("expand", true)); - - std::set<DicomTag> requestedTags; - OrthancRestApi::GetRequestedTags(requestedTags, call); - - ResourceFinder finder(resourceType, expand); - finder.AddRequestedTags(requestedTags); - - if (call.HasArgument("limit") || - call.HasArgument("since")) + if (!call.HasArgument("limit")) { - if (!call.HasArgument("limit")) - { - throw OrthancException(ErrorCode_BadRequest, - "Missing \"limit\" argument for GET request against: " + - call.FlattenUri()); - } - - if (!call.HasArgument("since")) - { - throw OrthancException(ErrorCode_BadRequest, - "Missing \"since\" argument for GET request against: " + - call.FlattenUri()); - } - - uint64_t since = boost::lexical_cast<uint64_t>(call.GetArgument("since", "")); - uint64_t limit = boost::lexical_cast<uint64_t>(call.GetArgument("limit", "")); - finder.SetLimitsSince(since); - finder.SetLimitsCount(limit); + throw OrthancException(ErrorCode_BadRequest, + "Missing \"limit\" argument for GET request against: " + + call.FlattenUri()); } - Json::Value answer; - finder.Execute(answer, context, OrthancRestApi::GetDicomFormat(call, DicomToJsonFormat_Human), false /* no "Metadata" field */); - call.GetOutput().AnswerJson(answer); - } - else - { - /** - * VERSION IN ORTHANC <= 1.12.4 - **/ - - std::list<std::string> result; - - std::set<DicomTag> requestedTags; - OrthancRestApi::GetRequestedTags(requestedTags, call); - - if (call.HasArgument("limit") || - call.HasArgument("since")) + if (!call.HasArgument("since")) { - if (!call.HasArgument("limit")) - { - throw OrthancException(ErrorCode_BadRequest, - "Missing \"limit\" argument for GET request against: " + - call.FlattenUri()); - } - - if (!call.HasArgument("since")) - { - throw OrthancException(ErrorCode_BadRequest, - "Missing \"since\" argument for GET request against: " + - call.FlattenUri()); - } - - size_t since = boost::lexical_cast<size_t>(call.GetArgument("since", "")); - size_t limit = boost::lexical_cast<size_t>(call.GetArgument("limit", "")); - index.GetAllUuids(result, resourceType, since, limit); + throw OrthancException(ErrorCode_BadRequest, + "Missing \"since\" argument for GET request against: " + + call.FlattenUri()); } - else - { - index.GetAllUuids(result, resourceType); - } - - AnswerListOfResources2(call.GetOutput(), context, result, resourceType, call.HasArgument("expand") && call.GetBooleanArgument("expand", true), - OrthancRestApi::GetDicomFormat(call, DicomToJsonFormat_Human), - requestedTags, - true /* allowStorageAccess */); + + uint64_t since = boost::lexical_cast<uint64_t>(call.GetArgument("since", "")); + uint64_t limit = boost::lexical_cast<uint64_t>(call.GetArgument("limit", "")); + finder.SetLimitsSince(since); + finder.SetLimitsCount(limit); } + + Json::Value answer; + finder.Execute(answer, OrthancRestApi::GetContext(call), + OrthancRestApi::GetDicomFormat(call, DicomToJsonFormat_Human), false /* no "Metadata" field */); + call.GetOutput().AnswerJson(answer); } @@ -352,39 +230,19 @@ return; } - const DicomToJsonFormat format = OrthancRestApi::GetDicomFormat(call, DicomToJsonFormat_Human); - std::set<DicomTag> requestedTags; OrthancRestApi::GetRequestedTags(requestedTags, call); - if (true) + const DicomToJsonFormat format = OrthancRestApi::GetDicomFormat(call, DicomToJsonFormat_Human); + + ResourceFinder finder(resourceType, true /* expand */); + finder.AddRequestedTags(requestedTags); + finder.SetOrthancId(resourceType, call.GetUriComponent("id", "")); + + Json::Value json; + if (finder.ExecuteOneResource(json, OrthancRestApi::GetContext(call), format, false /* no "Metadata" field */)) { - /** - * EXPERIMENTAL VERSION - **/ - - ResourceFinder finder(resourceType, true /* expand */); - finder.AddRequestedTags(requestedTags); - finder.SetOrthancId(resourceType, call.GetUriComponent("id", "")); - - Json::Value json; - if (finder.ExecuteOneResource(json, OrthancRestApi::GetContext(call), format, false /* no "Metadata" field */)) - { - call.GetOutput().AnswerJson(json); - } - } - else - { - /** - * VERSION IN ORTHANC <= 1.12.4 - **/ - - Json::Value json; - if (OrthancRestApi::GetContext(call).ExpandResource( - json, call.GetUriComponent("id", ""), resourceType, format, requestedTags, true /* allowStorageAccess */)) - { - call.GetOutput().AnswerJson(json); - } + call.GetOutput().AnswerJson(json); } } @@ -3173,70 +3031,6 @@ } - namespace - { - class FindVisitor : public ServerContext::ILookupVisitor - { - private: - bool isComplete_; - std::list<std::string> resources_; - FindStorageAccessMode findStorageAccessMode_; - - // cache the data we used during lookup and that we could reuse when building the answers - std::map<std::string, std::string> instancesIds_; // the id of an instance for each found resource. - std::map<std::string, boost::shared_ptr<DicomMap> > resourcesMainDicomTags_; // all tags read from DB for a resource (current level and upper levels) - std::map<std::string, boost::shared_ptr<Json::Value> > resourcesDicomAsJson_; // the dicom-as-json for a resource - - DicomToJsonFormat format_; - - public: - explicit FindVisitor(DicomToJsonFormat format, FindStorageAccessMode findStorageAccessMode) : - isComplete_(false), - findStorageAccessMode_(findStorageAccessMode), - format_(format) - { - } - - virtual bool IsDicomAsJsonNeeded() const ORTHANC_OVERRIDE - { - return false; // (*) - } - - virtual void MarkAsComplete() ORTHANC_OVERRIDE - { - isComplete_ = true; // Unused information as of Orthanc 1.5.0 - } - - virtual void Visit(const std::string& publicId, - const std::string& instanceId, - const DicomMap& mainDicomTags, - const Json::Value* dicomAsJson) ORTHANC_OVERRIDE - { - resources_.push_back(publicId); - instancesIds_[publicId] = instanceId; - resourcesMainDicomTags_[publicId].reset(mainDicomTags.Clone()); - if (dicomAsJson != NULL) - { - resourcesDicomAsJson_[publicId].reset(new Json::Value(*dicomAsJson)); // keep our own copy because we might reuse it between lookup and answers - } - else - { - resourcesDicomAsJson_[publicId] = boost::shared_ptr<Json::Value>(); - } - } - - void Answer(RestApiOutput& output, - ServerContext& context, - ResourceType level, - bool expand, - const std::set<DicomTag>& requestedTags) const - { - AnswerListOfResources1(output, context, resources_, instancesIds_, resourcesMainDicomTags_, resourcesDicomAsJson_, level, expand, format_, requestedTags, IsStorageAccessAllowedForAnswers(findStorageAccessMode_)); - } - }; - } - - static void Find(RestApiPostCall& call) { static const char* const KEY_CASE_SENSITIVE = "CaseSensitive"; @@ -3343,12 +3137,8 @@ throw OrthancException(ErrorCode_BadRequest, "Field \"" + std::string(KEY_LABELS_CONSTRAINT) + "\" must be an array of strings"); } - else if (true) + else { - /** - * EXPERIMENTAL VERSION - **/ - bool expand = false; if (request.isMember(KEY_EXPAND)) { @@ -3472,125 +3262,6 @@ finder.Execute(answer, context, format, false /* no "Metadata" field */); call.GetOutput().AnswerJson(answer); } - else - { - /** - * VERSION IN ORTHANC <= 1.12.4 - **/ - bool expand = false; - if (request.isMember(KEY_EXPAND)) - { - expand = request[KEY_EXPAND].asBool(); - } - - bool caseSensitive = false; - if (request.isMember(KEY_CASE_SENSITIVE)) - { - caseSensitive = request[KEY_CASE_SENSITIVE].asBool(); - } - - size_t limit = 0; - if (request.isMember(KEY_LIMIT)) - { - int tmp = request[KEY_LIMIT].asInt(); - if (tmp < 0) - { - throw OrthancException(ErrorCode_ParameterOutOfRange, - "Field \"" + std::string(KEY_LIMIT) + "\" must be a positive integer"); - } - - limit = static_cast<size_t>(tmp); - } - - size_t since = 0; - if (request.isMember(KEY_SINCE)) - { - int tmp = request[KEY_SINCE].asInt(); - if (tmp < 0) - { - throw OrthancException(ErrorCode_ParameterOutOfRange, - "Field \"" + std::string(KEY_SINCE) + "\" must be a positive integer"); - } - - since = static_cast<size_t>(tmp); - } - - std::set<DicomTag> requestedTags; - - if (request.isMember(KEY_REQUESTED_TAGS)) - { - FromDcmtkBridge::ParseListOfTags(requestedTags, request[KEY_REQUESTED_TAGS]); - } - - ResourceType level = StringToResourceType(request[KEY_LEVEL].asCString()); - - DatabaseLookup query; - - Json::Value::Members members = request[KEY_QUERY].getMemberNames(); - for (size_t i = 0; i < members.size(); i++) - { - if (request[KEY_QUERY][members[i]].type() != Json::stringValue) - { - throw OrthancException(ErrorCode_BadRequest, - "Tag \"" + members[i] + "\" must be associated with a string"); - } - - const std::string value = request[KEY_QUERY][members[i]].asString(); - - if (!value.empty()) - { - // An empty string corresponds to an universal constraint, - // so we ignore it. This mimics the behavior of class - // "OrthancFindRequestHandler" - query.AddRestConstraint(FromDcmtkBridge::ParseTag(members[i]), - value, caseSensitive, true); - } - } - - std::set<std::string> labels; - - if (request.isMember(KEY_LABELS)) // New in Orthanc 1.12.0 - { - for (Json::Value::ArrayIndex i = 0; i < request[KEY_LABELS].size(); i++) - { - if (request[KEY_LABELS][i].type() != Json::stringValue) - { - throw OrthancException(ErrorCode_BadRequest, "Field \"" + std::string(KEY_LABELS) + "\" must contain strings"); - } - else - { - labels.insert(request[KEY_LABELS][i].asString()); - } - } - } - - LabelsConstraint labelsConstraint = LabelsConstraint_All; - - if (request.isMember(KEY_LABELS_CONSTRAINT)) - { - const std::string& s = request[KEY_LABELS_CONSTRAINT].asString(); - if (s == "All") - { - labelsConstraint = LabelsConstraint_All; - } - else if (s == "Any") - { - labelsConstraint = LabelsConstraint_Any; - } - else if (s == "None") - { - labelsConstraint = LabelsConstraint_None; - } - else - { - throw OrthancException(ErrorCode_BadRequest, "Field \"" + std::string(KEY_LABELS_CONSTRAINT) + "\" must be \"All\", \"Any\", or \"None\""); - } - } - - FindVisitor visitor(OrthancRestApi::GetDicomFormat(request, DicomToJsonFormat_Human), context.GetFindStorageAccessMode()); - context.Apply(visitor, query, level, labels, labelsConstraint, since, limit); - visitor.Answer(call.GetOutput(), context, level, expand, requestedTags); - } } @@ -3618,9 +3289,6 @@ return; } - ServerIndex& index = OrthancRestApi::GetIndex(call); - ServerContext& context = OrthancRestApi::GetContext(call); - const bool expand = (!call.HasArgument("expand") || // this "expand" is the only one to have a false default value to keep backward compatibility call.GetBooleanArgument("expand", false)); @@ -3629,48 +3297,13 @@ std::set<DicomTag> requestedTags; OrthancRestApi::GetRequestedTags(requestedTags, call); - if (true) - { - /** - * EXPERIMENTAL VERSION - **/ - - ResourceFinder finder(end, expand); - finder.SetOrthancId(start, call.GetUriComponent("id", "")); - finder.AddRequestedTags(requestedTags); - - Json::Value answer; - finder.Execute(answer, context, format, false /* no "Metadata" field */); - call.GetOutput().AnswerJson(answer); - } - else - { - /** - * VERSION IN ORTHANC <= 1.12.4 - **/ - std::list<std::string> a, b, c; - a.push_back(call.GetUriComponent("id", "")); - - ResourceType type = start; - while (type != end) - { - b.clear(); - - for (std::list<std::string>::const_iterator - it = a.begin(); it != a.end(); ++it) - { - index.GetChildren(c, *it); - b.splice(b.begin(), c); - } - - type = GetChildResourceType(type); - - a.clear(); - a.splice(a.begin(), b); - } - - AnswerListOfResources2(call.GetOutput(), context, a, type, expand, format, requestedTags, true /* allowStorageAccess */); - } + ResourceFinder finder(end, expand); + finder.SetOrthancId(start, call.GetUriComponent("id", "")); + finder.AddRequestedTags(requestedTags); + + Json::Value answer; + finder.Execute(answer, OrthancRestApi::GetContext(call), format, false /* no "Metadata" field */); + call.GetOutput().AnswerJson(answer); } @@ -3783,26 +3416,9 @@ const DicomToJsonFormat format = OrthancRestApi::GetDicomFormat(call, DicomToJsonFormat_Human); Json::Value resource; - - if (true) + if (ExpandResource(resource, OrthancRestApi::GetContext(call), currentType, current, format, false)) { - /** - * EXPERIMENTAL VERSION - **/ - if (ExpandResource(resource, OrthancRestApi::GetContext(call), currentType, current, format, false)) - { - call.GetOutput().AnswerJson(resource); - } - } - else - { - /** - * VERSION IN ORTHANC <= 1.12.4 - **/ - if (OrthancRestApi::GetContext(call).ExpandResource(resource, current, end, format, requestedTags, true /* allowStorageAccess */)) - { - call.GetOutput().AnswerJson(resource); - } + call.GetOutput().AnswerJson(resource); } } @@ -4079,24 +3695,6 @@ } - static void AddMetadata(Json::Value& target, - ServerIndex& index, - const std::string& resource, - ResourceType level) - { - target = Json::objectValue; - - std::map<MetadataType, std::string> content; - index.GetAllMetadata(content, resource, level); - - for (std::map<MetadataType, std::string>::const_iterator - it = content.begin(); it != content.end(); ++it) - { - target[EnumerationToString(it->first)] = it->second; - } - } - - static void BulkContent(RestApiPostCall& call) { static const char* const LEVEL = "Level"; @@ -4233,34 +3831,10 @@ for (std::set<std::string>::const_iterator it = interest.begin(); it != interest.end(); ++it) { - if (true) - { - /** - * EXPERIMENTAL VERSION - **/ - Json::Value item; - if (ExpandResource(item, OrthancRestApi::GetContext(call), level, *it, format, metadata)) - { - answer.append(item); - } - } - else + Json::Value item; + if (ExpandResource(item, OrthancRestApi::GetContext(call), level, *it, format, metadata)) { - /** - * VERSION IN ORTHANC <= 1.12.4 - **/ - Json::Value item; - std::set<DicomTag> emptyRequestedTags; // not supported for bulk content - - if (OrthancRestApi::GetContext(call).ExpandResource(item, *it, level, format, emptyRequestedTags, true /* allowStorageAccess */)) - { - if (metadata) - { - AddMetadata(item[METADATA], index, *it, level); - } - - answer.append(item); - } + answer.append(item); } } } @@ -4275,38 +3849,15 @@ { ResourceType level; Json::Value item; - std::set<DicomTag> emptyRequestedTags; // not supported for bulk content - - if (true) + + if (index.LookupResourceType(level, *it) && + ExpandResource(item, OrthancRestApi::GetContext(call), level, *it, format, metadata)) { - /** - * EXPERIMENTAL VERSION - **/ - if (index.LookupResourceType(level, *it) && - ExpandResource(item, OrthancRestApi::GetContext(call), level, *it, format, metadata)) - { - answer.append(item); - } + answer.append(item); } else { - /** - * VERSION IN ORTHANC <= 1.12.4 - **/ - if (index.LookupResourceType(level, *it) && - OrthancRestApi::GetContext(call).ExpandResource(item, *it, level, format, emptyRequestedTags, true /* allowStorageAccess */)) - { - if (metadata) - { - AddMetadata(item[METADATA], index, *it, level); - } - - answer.append(item); - } - else - { - CLOG(INFO, HTTP) << "Unknown resource during a bulk content retrieval: " << *it; - } + CLOG(INFO, HTTP) << "Unknown resource during a bulk content retrieval: " << *it; } } }
--- a/OrthancServer/Sources/OrthancWebDav.cpp Sat Jul 13 00:58:29 2024 +0200 +++ b/OrthancServer/Sources/OrthancWebDav.cpp Sat Jul 13 00:59:01 2024 +0200 @@ -86,99 +86,6 @@ } - class OrthancWebDav::DicomIdentifiersVisitor : public ServerContext::ILookupVisitor - { - private: - ServerContext& context_; - bool isComplete_; - Collection& target_; - ResourceType level_; - - public: - DicomIdentifiersVisitor(ServerContext& context, - Collection& target, - ResourceType level) : - context_(context), - isComplete_(false), - target_(target), - level_(level) - { - } - - virtual bool IsDicomAsJsonNeeded() const ORTHANC_OVERRIDE - { - return false; // (*) - } - - virtual void MarkAsComplete() ORTHANC_OVERRIDE - { - isComplete_ = true; // TODO - } - - virtual void Visit(const std::string& publicId, - const std::string& instanceId /* unused */, - const DicomMap& mainDicomTags, - const Json::Value* dicomAsJson /* unused (*) */) ORTHANC_OVERRIDE - { - DicomTag tag(0, 0); - MetadataType timeMetadata; - - switch (level_) - { - case ResourceType_Study: - tag = DICOM_TAG_STUDY_INSTANCE_UID; - timeMetadata = MetadataType_LastUpdate; - break; - - case ResourceType_Series: - tag = DICOM_TAG_SERIES_INSTANCE_UID; - timeMetadata = MetadataType_LastUpdate; - break; - - case ResourceType_Instance: - tag = DICOM_TAG_SOP_INSTANCE_UID; - timeMetadata = MetadataType_Instance_ReceptionDate; - break; - - default: - throw OrthancException(ErrorCode_InternalError); - } - - std::string s; - if (mainDicomTags.LookupStringValue(s, tag, false) && - !s.empty()) - { - std::unique_ptr<Resource> resource; - - if (level_ == ResourceType_Instance) - { - FileInfo info; - int64_t revision; // Ignored - if (context_.GetIndex().LookupAttachment(info, revision, publicId, FileContentType_Dicom)) - { - std::unique_ptr<File> f(new File(s + ".dcm")); - f->SetMimeType(MimeType_Dicom); - f->SetContentLength(info.GetUncompressedSize()); - resource.reset(f.release()); - } - } - else - { - resource.reset(new Folder(s)); - } - - if (resource.get() != NULL) - { - boost::posix_time::ptime t; - LookupTime(t, context_, publicId, level_, timeMetadata); - resource->SetCreationTime(t); - target_.AddResource(resource.release()); - } - } - } - }; - - class OrthancWebDav::DicomIdentifiersVisitorV2 : public ResourceFinder::IVisitor { private: @@ -271,58 +178,6 @@ }; - class OrthancWebDav::DicomFileVisitor : public ServerContext::ILookupVisitor - { - private: - ServerContext& context_; - bool success_; - std::string& target_; - boost::posix_time::ptime& time_; - - public: - DicomFileVisitor(ServerContext& context, - std::string& target, - boost::posix_time::ptime& time) : - context_(context), - success_(false), - target_(target), - time_(time) - { - } - - bool IsSuccess() const - { - return success_; - } - - virtual bool IsDicomAsJsonNeeded() const ORTHANC_OVERRIDE - { - return false; // (*) - } - - virtual void MarkAsComplete() ORTHANC_OVERRIDE - { - } - - virtual void Visit(const std::string& publicId, - const std::string& instanceId /* unused */, - const DicomMap& mainDicomTags, - const Json::Value* dicomAsJson /* unused (*) */) ORTHANC_OVERRIDE - { - if (success_) - { - success_ = false; // Two matches => Error - } - else - { - LookupTime(time_, context_, publicId, ResourceType_Instance, MetadataType_Instance_ReceptionDate); - context_.ReadDicom(target_, publicId); - success_ = true; - } - } - }; - - class OrthancWebDav::DicomFileVisitorV2 : public ResourceFinder::IVisitor { private: @@ -377,67 +232,6 @@ }; - class OrthancWebDav::OrthancJsonVisitor : public ServerContext::ILookupVisitor - { - private: - ServerContext& context_; - bool success_; - std::string& target_; - ResourceType level_; - - public: - OrthancJsonVisitor(ServerContext& context, - std::string& target, - ResourceType level) : - context_(context), - success_(false), - target_(target), - level_(level) - { - } - - bool IsSuccess() const - { - return success_; - } - - virtual bool IsDicomAsJsonNeeded() const ORTHANC_OVERRIDE - { - return false; // (*) - } - - virtual void MarkAsComplete() ORTHANC_OVERRIDE - { - } - - virtual void Visit(const std::string& publicId, - const std::string& instanceId /* unused */, - const DicomMap& mainDicomTags, - const Json::Value* dicomAsJson /* unused (*) */) ORTHANC_OVERRIDE - { - Json::Value resource; - std::set<DicomTag> emptyRequestedTags; // not supported for webdav - - if (context_.ExpandResource(resource, publicId, level_, DicomToJsonFormat_Human, emptyRequestedTags, true /* allowStorageAccess */)) - { - if (success_) - { - success_ = false; // Two matches => Error - } - else - { - target_ = resource.toStyledString(); - - // Replace UNIX newlines with DOS newlines - boost::replace_all(target_, "\n", "\r\n"); - - success_ = true; - } - } - } - }; - - class OrthancWebDav::ResourcesIndex : public boost::noncopyable { public: @@ -1569,12 +1363,11 @@ { DatabaseLookup query; ResourceType level; - size_t limit = 0; // By default, no limits if (path.size() == 1) { level = ResourceType_Study; - limit = 0; // TODO - Should we limit here? + // TODO - Should we limit here? } else if (path.size() == 2) { @@ -1599,47 +1392,31 @@ return false; } - if (true) - { - /** - * EXPERIMENTAL VERSION - **/ - - ResourceFinder finder(level, false /* don't expand */); - finder.SetDatabaseLookup(query); - finder.SetRetrieveMetadata(true); + ResourceFinder finder(level, false /* don't expand */); + finder.SetDatabaseLookup(query); + finder.SetRetrieveMetadata(true); - switch (level) - { - case ResourceType_Study: - finder.AddRequestedTag(DICOM_TAG_STUDY_INSTANCE_UID); - break; - - case ResourceType_Series: - finder.AddRequestedTag(DICOM_TAG_SERIES_INSTANCE_UID); - break; + switch (level) + { + case ResourceType_Study: + finder.AddRequestedTag(DICOM_TAG_STUDY_INSTANCE_UID); + break; - case ResourceType_Instance: - finder.AddRequestedTag(DICOM_TAG_SOP_INSTANCE_UID); - finder.SetRetrieveAttachments(true); - break; - - default: - throw OrthancException(ErrorCode_InternalError); - } + case ResourceType_Series: + finder.AddRequestedTag(DICOM_TAG_SERIES_INSTANCE_UID); + break; - DicomIdentifiersVisitorV2 visitor(collection); - finder.Execute(visitor, context_); + case ResourceType_Instance: + finder.AddRequestedTag(DICOM_TAG_SOP_INSTANCE_UID); + finder.SetRetrieveAttachments(true); + break; + + default: + throw OrthancException(ErrorCode_InternalError); } - else - { - /** - * VERSION IN ORTHANC <= 1.12.4 - **/ - DicomIdentifiersVisitor visitor(context_, collection, level); - context_.Apply(visitor, query, level, 0 /* since */, limit); - } + DicomIdentifiersVisitorV2 visitor(collection); + finder.Execute(visitor, context_); return true; } @@ -1707,23 +1484,7 @@ true /* case sensitive */, true /* mandatory tag */); mime = MimeType_Json; - - if (true) - { - /** - * EXPERIMENTAL VERSION - **/ - return GetOrthancJson(content, context_, ResourceType_Study, query); - } - else - { - /** - * VERSION IN ORTHANC <= 1.12.4 - **/ - OrthancJsonVisitor visitor(context_, content, ResourceType_Study); - context_.Apply(visitor, query, ResourceType_Study, 0 /* since */, 0 /* no limit */); - return visitor.IsSuccess(); - } + return GetOrthancJson(content, context_, ResourceType_Study, query); } else if (path.size() == 4 && path[3] == SERIES_INFO) @@ -1735,23 +1496,7 @@ true /* case sensitive */, true /* mandatory tag */); mime = MimeType_Json; - - if (true) - { - /** - * EXPERIMENTAL VERSION - **/ - return GetOrthancJson(content, context_, ResourceType_Series, query); - } - else - { - /** - * VERSION IN ORTHANC <= 1.12.4 - **/ - OrthancJsonVisitor visitor(context_, content, ResourceType_Series); - context_.Apply(visitor, query, ResourceType_Series, 0 /* since */, 0 /* no limit */); - return visitor.IsSuccess(); - } + return GetOrthancJson(content, context_, ResourceType_Series, query); } else if (path.size() == 4 && boost::ends_with(path[3], ".dcm")) @@ -1768,30 +1513,15 @@ mime = MimeType_Dicom; - if (true) - { - /** - * EXPERIMENTAL VERSION - **/ - ResourceFinder finder(ResourceType_Instance, false /* no expand */); - finder.SetDatabaseLookup(query); - finder.SetRetrieveMetadata(true); - finder.SetRetrieveAttachments(true); + ResourceFinder finder(ResourceType_Instance, false /* no expand */); + finder.SetDatabaseLookup(query); + finder.SetRetrieveMetadata(true); + finder.SetRetrieveAttachments(true); - DicomFileVisitorV2 visitor(context_, content, modificationTime); - finder.Execute(visitor, context_); + DicomFileVisitorV2 visitor(context_, content, modificationTime); + finder.Execute(visitor, context_); - return visitor.IsSuccess(); - } - else - { - /** - * VERSION IN ORTHANC <= 1.12.4 - **/ - DicomFileVisitor visitor(context_, content, modificationTime); - context_.Apply(visitor, query, ResourceType_Instance, 0 /* since */, 0 /* no limit */); - return visitor.IsSuccess(); - } + return visitor.IsSuccess(); } else {
--- a/OrthancServer/Sources/OrthancWebDav.h Sat Jul 13 00:58:29 2024 +0200 +++ b/OrthancServer/Sources/OrthancWebDav.h Sat Jul 13 00:59:01 2024 +0200 @@ -38,9 +38,7 @@ typedef std::map<ResourceType, std::string> Templates; class DicomDeleteVisitor; - class DicomFileVisitor; class DicomFileVisitorV2; - class DicomIdentifiersVisitor; class DicomIdentifiersVisitorV2; class InstancesOfSeries; class InternalNode;
--- a/OrthancServer/Sources/ServerContext.cpp Sat Jul 13 00:58:29 2024 +0200 +++ b/OrthancServer/Sources/ServerContext.cpp Sat Jul 13 00:59:01 2024 +0200 @@ -69,12 +69,6 @@ namespace Orthanc { - static void ComputeStudyTags(ExpandedResource& resource, - ServerContext& context, - const std::string& studyPublicId, - const std::set<DicomTag>& requestedTags); - - static bool IsUncompressedTransferSyntax(DicomTransferSyntax transferSyntax) { return (transferSyntax == DicomTransferSyntax_LittleEndianImplicit || @@ -1531,188 +1525,6 @@ } - void ServerContext::Apply(ILookupVisitor& visitor, - const DatabaseLookup& lookup, - ResourceType queryLevel, - const std::set<std::string>& labels, - LabelsConstraint labelsConstraint, - size_t since, - size_t limit) - { - const uint64_t databaseLimit = GetDatabaseLimits(queryLevel); - - std::vector<std::string> resources, instances; - const DicomTagConstraint* dicomModalitiesConstraint = NULL; - - bool hasModalitiesInStudyLookup = (queryLevel == ResourceType_Study && - lookup.GetConstraint(dicomModalitiesConstraint, DICOM_TAG_MODALITIES_IN_STUDY) && - ((dicomModalitiesConstraint->GetConstraintType() == ConstraintType_Equal && !dicomModalitiesConstraint->GetValue().empty()) || - (dicomModalitiesConstraint->GetConstraintType() == ConstraintType_List && !dicomModalitiesConstraint->GetValues().empty()))); - - std::unique_ptr<DatabaseLookup> fastLookup(lookup.Clone()); - - if (hasModalitiesInStudyLookup) - { - fastLookup->RemoveConstraint(DICOM_TAG_MODALITIES_IN_STUDY); - } - - const size_t lookupLimit = (databaseLimit == 0 ? 0 : databaseLimit + 1); - GetIndex().ApplyLookupResources(resources, &instances, *fastLookup, queryLevel, labels, labelsConstraint, lookupLimit); - - bool complete = (databaseLimit == 0 || - resources.size() <= databaseLimit); - - LOG(INFO) << "Number of candidate resources after fast DB filtering on main DICOM tags: " << resources.size(); - - /** - * "resources" contains the Orthanc ID of the resource at level - * "queryLevel", "instances" contains one the Orthanc ID of one - * sample instance from this resource. - **/ - assert(resources.size() == instances.size()); - - size_t countResults = 0; - size_t skipped = 0; - - const bool isDicomAsJsonNeeded = visitor.IsDicomAsJsonNeeded(); - - for (size_t i = 0; i < instances.size(); i++) - { - // Optimization in Orthanc 1.5.1 - Don't read the full JSON from - // the disk if only "main DICOM tags" are to be returned - - boost::shared_ptr<Json::Value> dicomAsJson; - - bool hasOnlyMainDicomTags; - DicomMap dicom; - DicomMap allMainDicomTagsFromDB; - - if (!IsStorageAccessAllowedForAnswers(findStorageAccessMode_) - || fastLookup->HasOnlyMainDicomTags()) - { - // Case (1): The main DICOM tags, as stored in the database, - // are sufficient to look for match - - if (!GetIndex().GetAllMainDicomTags(allMainDicomTagsFromDB, instances[i])) - { - // The instance has been removed during the execution of the - // lookup, ignore it - continue; - } - - // New in Orthanc 1.6.0: Only keep the main DICOM tags at the - // level of interest for the query - switch (queryLevel) - { - // WARNING: Don't reorder cases below, and don't add "break" - case ResourceType_Instance: - dicom.MergeMainDicomTags(allMainDicomTagsFromDB, ResourceType_Instance); - - case ResourceType_Series: - dicom.MergeMainDicomTags(allMainDicomTagsFromDB, ResourceType_Series); - - case ResourceType_Study: - dicom.MergeMainDicomTags(allMainDicomTagsFromDB, ResourceType_Study); - - case ResourceType_Patient: - dicom.MergeMainDicomTags(allMainDicomTagsFromDB, ResourceType_Patient); - break; - - default: - throw OrthancException(ErrorCode_InternalError); - } - - hasOnlyMainDicomTags = true; - } - else - { - // Case (2): Need to read the "DICOM-as-JSON" attachment from - // the storage area - dicomAsJson.reset(new Json::Value); - ReadDicomAsJson(*dicomAsJson, instances[i]); - - dicom.FromDicomAsJson(*dicomAsJson); - - // This map contains the entire JSON, i.e. more than the main DICOM tags - hasOnlyMainDicomTags = false; - } - - if (fastLookup->IsMatch(dicom)) - { - bool isMatch = true; - - if (hasModalitiesInStudyLookup) - { - std::set<DicomTag> requestedTags; - requestedTags.insert(DICOM_TAG_MODALITIES_IN_STUDY); - ExpandedResource resource; - ComputeStudyTags(resource, *this, resources[i], requestedTags); - - std::vector<std::string> modalities; - Toolbox::TokenizeString(modalities, resource.GetMainDicomTags().GetValue(DICOM_TAG_MODALITIES_IN_STUDY).GetContent(), '\\'); - bool hasAtLeastOneModalityMatching = false; - for (size_t m = 0; m < modalities.size(); m++) - { - hasAtLeastOneModalityMatching |= dicomModalitiesConstraint->IsMatch(modalities[m]); - } - - isMatch = isMatch && hasAtLeastOneModalityMatching; - // copy the value of ModalitiesInStudy such that it can be reused to build the answer - allMainDicomTagsFromDB.SetValue(DICOM_TAG_MODALITIES_IN_STUDY, resource.GetMainDicomTags().GetValue(DICOM_TAG_MODALITIES_IN_STUDY)); - } - - if (isMatch) - { - if (skipped < since) - { - skipped++; - } - else if (limit != 0 && - countResults >= limit) - { - // Too many results, don't mark as complete - complete = false; - break; - } - else - { - if (IsStorageAccessAllowedForAnswers(findStorageAccessMode_) && - dicomAsJson.get() == NULL && - isDicomAsJsonNeeded) - { - dicomAsJson.reset(new Json::Value); - ReadDicomAsJson(*dicomAsJson, instances[i]); - } - - if (hasOnlyMainDicomTags) - { - // This is Case (1): The variable "dicom" only contains the main DICOM tags - visitor.Visit(resources[i], instances[i], allMainDicomTagsFromDB, dicomAsJson.get()); - } - else - { - // Remove the non-main DICOM tags from "dicom" if Case (2) - // was used, for consistency with Case (1) - - DicomMap mainDicomTags; - mainDicomTags.ExtractMainDicomTags(dicom); - visitor.Visit(resources[i], instances[i], mainDicomTags, dicomAsJson.get()); - } - - countResults ++; - } - } - } - } - - if (complete) - { - visitor.MarkAsComplete(); - } - - LOG(INFO) << "Number of matching resources: " << countResults; - } - bool ServerContext::LookupOrReconstructMetadata(std::string& target, const std::string& publicId, ResourceType level, @@ -2121,600 +1933,6 @@ isUnknownSopClassAccepted_ = accepted; } - - static void SerializeExpandedResource(Json::Value& target, - const ExpandedResource& resource, - DicomToJsonFormat format, - const std::set<DicomTag>& requestedTags, - ExpandResourceFlags expandFlags) - { - target = Json::objectValue; - - target["Type"] = GetResourceTypeText(resource.GetLevel(), false, true); - target["ID"] = resource.GetPublicId(); - - if (!resource.parentId_.empty()) - { - switch (resource.GetLevel()) - { - case ResourceType_Patient: - break; - - case ResourceType_Study: - target["ParentPatient"] = resource.parentId_; - break; - - case ResourceType_Series: - target["ParentStudy"] = resource.parentId_; - break; - - case ResourceType_Instance: - target["ParentSeries"] = resource.parentId_; - break; - - default: - throw OrthancException(ErrorCode_InternalError); - } - } - - if ((expandFlags & ExpandResourceFlags_IncludeChildren) != 0) - { - switch (resource.GetLevel()) - { - case ResourceType_Patient: - case ResourceType_Study: - case ResourceType_Series: - { - Json::Value c = Json::arrayValue; - - for (std::list<std::string>::const_iterator - it = resource.childrenIds_.begin(); it != resource.childrenIds_.end(); ++it) - { - c.append(*it); - } - - if (resource.GetLevel() == ResourceType_Patient) - { - target["Studies"] = c; - } - else if (resource.GetLevel() == ResourceType_Study) - { - target["Series"] = c; - } - else - { - target["Instances"] = c; - } - break; - } - - case ResourceType_Instance: - break; - - default: - throw OrthancException(ErrorCode_InternalError); - } - } - - if ((expandFlags & ExpandResourceFlags_IncludeMetadata) != 0) - { - switch (resource.GetLevel()) - { - case ResourceType_Patient: - case ResourceType_Study: - break; - - case ResourceType_Series: - if (resource.expectedNumberOfInstances_ < 0) - { - target["ExpectedNumberOfInstances"] = Json::nullValue; - } - else - { - target["ExpectedNumberOfInstances"] = resource.expectedNumberOfInstances_; - } - target["Status"] = resource.status_; - break; - - case ResourceType_Instance: - { - target["FileSize"] = static_cast<unsigned int>(resource.fileSize_); - target["FileUuid"] = resource.fileUuid_; - - if (resource.indexInSeries_ < 0) - { - target["IndexInSeries"] = Json::nullValue; - } - else - { - target["IndexInSeries"] = resource.indexInSeries_; - } - - break; - } - - default: - throw OrthancException(ErrorCode_InternalError); - } - - if (!resource.anonymizedFrom_.empty()) - { - target["AnonymizedFrom"] = resource.anonymizedFrom_; - } - - if (!resource.modifiedFrom_.empty()) - { - target["ModifiedFrom"] = resource.modifiedFrom_; - } - } - - if (resource.GetLevel() == ResourceType_Patient || - resource.GetLevel() == ResourceType_Study || - resource.GetLevel() == ResourceType_Series) - { - if ((expandFlags & ExpandResourceFlags_IncludeIsStable) != 0) - { - target["IsStable"] = resource.isStable_; - } - - if (!resource.lastUpdate_.empty()) - { - target["LastUpdate"] = resource.lastUpdate_; - } - } - - if ((expandFlags & ExpandResourceFlags_IncludeMainDicomTags) != 0) - { - // serialize tags - - static const char* const MAIN_DICOM_TAGS = "MainDicomTags"; - static const char* const PATIENT_MAIN_DICOM_TAGS = "PatientMainDicomTags"; - - DicomMap mainDicomTags; - resource.GetMainDicomTags().ExtractResourceInformation(mainDicomTags, resource.GetLevel()); - - target[MAIN_DICOM_TAGS] = Json::objectValue; - FromDcmtkBridge::ToJson(target[MAIN_DICOM_TAGS], mainDicomTags, format); - - if (resource.GetLevel() == ResourceType_Study) - { - DicomMap patientMainDicomTags; - resource.GetMainDicomTags().ExtractPatientInformation(patientMainDicomTags); - - target[PATIENT_MAIN_DICOM_TAGS] = Json::objectValue; - FromDcmtkBridge::ToJson(target[PATIENT_MAIN_DICOM_TAGS], patientMainDicomTags, format); - } - - if (requestedTags.size() > 0) - { - static const char* const REQUESTED_TAGS = "RequestedTags"; - - DicomMap tags; - resource.GetMainDicomTags().ExtractTags(tags, requestedTags); - - target[REQUESTED_TAGS] = Json::objectValue; - FromDcmtkBridge::ToJson(target[REQUESTED_TAGS], tags, format); - - } - } - - if ((expandFlags & ExpandResourceFlags_IncludeLabels) != 0) - { - Json::Value labels = Json::arrayValue; - - for (std::set<std::string>::const_iterator it = resource.labels_.begin(); it != resource.labels_.end(); ++it) - { - labels.append(*it); - } - - target["Labels"] = labels; - } - - // new in Orthanc 1.12.4 - if ((expandFlags & ExpandResourceFlags_IncludeAllMetadata) != 0) - { - Json::Value metadata = Json::objectValue; - - for (std::map<MetadataType, std::string>::const_iterator it = resource.metadata_.begin(); it != resource.metadata_.end(); ++it) - { - metadata[EnumerationToString(it->first)] = it->second; - } - - target["Metadata"] = metadata; - } - } - - - static void ComputeInstanceTags(ExpandedResource& resource, - ServerContext& context, - const std::string& instancePublicId, - const std::set<DicomTag>& requestedTags) - { - if (requestedTags.count(DICOM_TAG_INSTANCE_AVAILABILITY) > 0) - { - resource.GetMainDicomTags().SetValue(DICOM_TAG_INSTANCE_AVAILABILITY, "ONLINE", false); - resource.missingRequestedTags_.erase(DICOM_TAG_INSTANCE_AVAILABILITY); - } - } - - - static void ComputeSeriesTags(ExpandedResource& resource, - ServerContext& context, - const std::string& seriesPublicId, - const std::set<DicomTag>& requestedTags) - { - if (requestedTags.count(DICOM_TAG_NUMBER_OF_SERIES_RELATED_INSTANCES) > 0) - { - ServerIndex& index = context.GetIndex(); - std::list<std::string> instances; - - index.GetChildren(instances, seriesPublicId); - - resource.GetMainDicomTags().SetValue(DICOM_TAG_NUMBER_OF_SERIES_RELATED_INSTANCES, - boost::lexical_cast<std::string>(instances.size()), false); - resource.missingRequestedTags_.erase(DICOM_TAG_NUMBER_OF_SERIES_RELATED_INSTANCES); - } - } - - static void ComputeStudyTags(ExpandedResource& resource, - ServerContext& context, - const std::string& studyPublicId, - const std::set<DicomTag>& requestedTags) - { - ServerIndex& index = context.GetIndex(); - std::list<std::string> series; - std::list<std::string> instances; - - bool hasNbRelatedSeries = requestedTags.count(DICOM_TAG_NUMBER_OF_STUDY_RELATED_SERIES) > 0; - bool hasNbRelatedInstances = requestedTags.count(DICOM_TAG_NUMBER_OF_STUDY_RELATED_INSTANCES) > 0; - bool hasModalitiesInStudy = requestedTags.count(DICOM_TAG_MODALITIES_IN_STUDY) > 0; - bool hasSopClassesInStudy = requestedTags.count(DICOM_TAG_SOP_CLASSES_IN_STUDY) > 0; - - index.GetChildren(series, studyPublicId); - - if (hasModalitiesInStudy) - { - std::set<std::string> values; - - for (std::list<std::string>::const_iterator - it = series.begin(); it != series.end(); ++it) - { - DicomMap tags; - index.GetMainDicomTags(tags, *it, ResourceType_Series, ResourceType_Series); - - const DicomValue* value = tags.TestAndGetValue(DICOM_TAG_MODALITY); - - if (value != NULL && - !value->IsNull() && - !value->IsBinary()) - { - values.insert(value->GetContent()); - } - } - - std::string modalities; - Toolbox::JoinStrings(modalities, values, "\\"); - - resource.GetMainDicomTags().SetValue(DICOM_TAG_MODALITIES_IN_STUDY, modalities, false); - resource.missingRequestedTags_.erase(DICOM_TAG_MODALITIES_IN_STUDY); - } - - if (hasNbRelatedSeries) - { - resource.GetMainDicomTags().SetValue(DICOM_TAG_NUMBER_OF_STUDY_RELATED_SERIES, - boost::lexical_cast<std::string>(series.size()), false); - resource.missingRequestedTags_.erase(DICOM_TAG_NUMBER_OF_STUDY_RELATED_SERIES); - } - - if (hasNbRelatedInstances || hasSopClassesInStudy) - { - for (std::list<std::string>::const_iterator - it = series.begin(); it != series.end(); ++it) - { - std::list<std::string> seriesInstancesIds; - index.GetChildren(seriesInstancesIds, *it); - - instances.splice(instances.end(), seriesInstancesIds); - } - - if (hasNbRelatedInstances) - { - resource.GetMainDicomTags().SetValue(DICOM_TAG_NUMBER_OF_STUDY_RELATED_INSTANCES, - boost::lexical_cast<std::string>(instances.size()), false); - resource.missingRequestedTags_.erase(DICOM_TAG_NUMBER_OF_STUDY_RELATED_INSTANCES); - } - - if (hasSopClassesInStudy) - { - std::set<std::string> values; - - for (std::list<std::string>::const_iterator - it = instances.begin(); it != instances.end(); ++it) - { - std::string value; - - if (context.LookupOrReconstructMetadata(value, *it, ResourceType_Instance, MetadataType_Instance_SopClassUid)) - { - values.insert(value); - } - } - - if (values.size() > 0) - { - std::string sopClassUids; - Toolbox::JoinStrings(sopClassUids, values, "\\"); - resource.GetMainDicomTags().SetValue(DICOM_TAG_SOP_CLASSES_IN_STUDY, sopClassUids, false); - } - - resource.missingRequestedTags_.erase(DICOM_TAG_SOP_CLASSES_IN_STUDY); - } - } - } - - static void ComputePatientTags(ExpandedResource& resource, - ServerContext& context, - const std::string& patientPublicId, - const std::set<DicomTag>& requestedTags) - { - ServerIndex& index = context.GetIndex(); - - std::list<std::string> studies; - std::list<std::string> series; - std::list<std::string> instances; - - bool hasNbRelatedStudies = requestedTags.count(DICOM_TAG_NUMBER_OF_PATIENT_RELATED_STUDIES) > 0; - bool hasNbRelatedSeries = requestedTags.count(DICOM_TAG_NUMBER_OF_PATIENT_RELATED_SERIES) > 0; - bool hasNbRelatedInstances = requestedTags.count(DICOM_TAG_NUMBER_OF_PATIENT_RELATED_INSTANCES) > 0; - - index.GetChildren(studies, patientPublicId); - - if (hasNbRelatedStudies) - { - resource.GetMainDicomTags().SetValue(DICOM_TAG_NUMBER_OF_PATIENT_RELATED_STUDIES, - boost::lexical_cast<std::string>(studies.size()), false); - resource.missingRequestedTags_.erase(DICOM_TAG_NUMBER_OF_PATIENT_RELATED_STUDIES); - } - - if (hasNbRelatedSeries || hasNbRelatedInstances) - { - for (std::list<std::string>::const_iterator - it = studies.begin(); it != studies.end(); ++it) - { - std::list<std::string> thisSeriesIds; - index.GetChildren(thisSeriesIds, *it); - series.splice(series.end(), thisSeriesIds); - } - - if (hasNbRelatedSeries) - { - resource.GetMainDicomTags().SetValue(DICOM_TAG_NUMBER_OF_PATIENT_RELATED_SERIES, - boost::lexical_cast<std::string>(series.size()), false); - resource.missingRequestedTags_.erase(DICOM_TAG_NUMBER_OF_PATIENT_RELATED_SERIES); - } - } - - if (hasNbRelatedInstances) - { - for (std::list<std::string>::const_iterator - it = series.begin(); it != series.end(); ++it) - { - std::list<std::string> thisInstancesIds; - index.GetChildren(thisInstancesIds, *it); - instances.splice(instances.end(), thisInstancesIds); - } - - resource.GetMainDicomTags().SetValue(DICOM_TAG_NUMBER_OF_PATIENT_RELATED_INSTANCES, - boost::lexical_cast<std::string>(instances.size()), false); - resource.missingRequestedTags_.erase(DICOM_TAG_NUMBER_OF_PATIENT_RELATED_INSTANCES); - } - } - - - static void ComputeTags(ExpandedResource& resource, - ServerContext& context, - const std::string& resourceId, - ResourceType level, - const std::set<DicomTag>& requestedTags) - { - if (level == ResourceType_Patient - && DicomMap::HasComputedTags(resource.missingRequestedTags_, ResourceType_Patient)) - { - ComputePatientTags(resource, context, resourceId, requestedTags); - } - - if (level == ResourceType_Study - && DicomMap::HasComputedTags(resource.missingRequestedTags_, ResourceType_Study)) - { - ComputeStudyTags(resource, context, resourceId, requestedTags); - } - - if (level == ResourceType_Series - && DicomMap::HasComputedTags(resource.missingRequestedTags_, ResourceType_Series)) - { - ComputeSeriesTags(resource, context, resourceId, requestedTags); - } - - if (level == ResourceType_Instance - && DicomMap::HasComputedTags(resource.missingRequestedTags_, ResourceType_Instance)) - { - ComputeInstanceTags(resource, context, resourceId, requestedTags); - } - } - - bool ServerContext::ExpandResource(Json::Value& target, - const std::string& publicId, - ResourceType level, - DicomToJsonFormat format, - const std::set<DicomTag>& requestedTags, - bool allowStorageAccess) - { - std::string unusedInstanceId; - Json::Value* unusedDicomAsJson = NULL; - DicomMap unusedMainDicomTags; - - return ExpandResource(target, publicId, unusedMainDicomTags, unusedInstanceId, unusedDicomAsJson, level, format, requestedTags, allowStorageAccess); - } - - bool ServerContext::ExpandResource(Json::Value& target, - const std::string& publicId, - const DicomMap& mainDicomTags, // optional: the main dicom tags for the resource (if already available) - const std::string& instanceId, // optional: the id of an instance for the resource (if already available) - const Json::Value* dicomAsJson, // optional: the dicom-as-json for the resource (if already available) - ResourceType level, - DicomToJsonFormat format, - const std::set<DicomTag>& requestedTags, - bool allowStorageAccess) - { - ExpandedResource resource; - - if (ExpandResource(resource, publicId, mainDicomTags, instanceId, dicomAsJson, level, requestedTags, ExpandResourceFlags_DefaultExtract, allowStorageAccess)) - { - SerializeExpandedResource(target, resource, format, requestedTags, ExpandResourceFlags_DefaultOutput); - return true; - } - - return false; - } - - bool ServerContext::ExpandResource(ExpandedResource& resource, - const std::string& publicId, - const DicomMap& mainDicomTags, // optional: the main dicom tags for the resource (if already available) - const std::string& instanceId, // optional: the id of an instance for the resource (if already available) - const Json::Value* dicomAsJson, // optional: the dicom-as-json for the resource (if already available) - ResourceType level, - const std::set<DicomTag>& requestedTags, - ExpandResourceFlags expandFlags, - bool allowStorageAccess) - { - // first try to get the tags from what is already available - - if ((expandFlags & ExpandResourceFlags_IncludeMainDicomTags) && - mainDicomTags.GetSize() > 0 && - dicomAsJson != NULL) - { - - resource.GetMainDicomTags().Merge(mainDicomTags); - - if (dicomAsJson->isObject()) - { - resource.GetMainDicomTags().FromDicomAsJson(*dicomAsJson); - } - - std::set<DicomTag> retrievedTags; - std::set<DicomTag> missingTags; - resource.GetMainDicomTags().GetTags(retrievedTags); - - Toolbox::GetMissingsFromSet(missingTags, requestedTags, retrievedTags); - - // if all possible tags have been read, no need to get them from DB anymore - if (missingTags.size() > 0 && DicomMap::HasOnlyComputedTags(missingTags)) - { - resource.missingRequestedTags_ = missingTags; - ComputeTags(resource, *this, publicId, level, requestedTags); - return true; - } - else if (missingTags.size() == 0) - { - expandFlags = static_cast<ExpandResourceFlags>(expandFlags & ~ExpandResourceFlags_IncludeMainDicomTags); - } - - if (missingTags.size() == 0 && expandFlags == ExpandResourceFlags_None) // we have already retrieved anything we need - { - return true; - } - } - - if (expandFlags != ExpandResourceFlags_None && - GetIndex().ExpandResource(resource, publicId, level, requestedTags, - static_cast<ExpandResourceFlags>(expandFlags | ExpandResourceFlags_IncludeMetadata))) // we always need the metadata to get the mainDicomTagsSignature - { - // check the main dicom tags list has not changed since the resource was stored - if (resource.mainDicomTagsSignature_ != DicomMap::GetMainDicomTagsSignature(resource.GetLevel())) - { - OrthancConfiguration::ReaderLock lock; - if (lock.GetConfiguration().IsWarningEnabled(Warnings_002_InconsistentDicomTagsInDb)) - { - LOG(WARNING) << "W002: " << Orthanc::GetResourceTypeText(resource.GetLevel(), false , false) - << " has been stored with another version of Main Dicom Tags list, you should POST to /" - << Orthanc::GetResourceTypeText(resource.GetLevel(), true, false) - << "/" << resource.GetPublicId() - << "/reconstruct to update the list of tags saved in DB. Some MainDicomTags might be missing from this answer."; - } - } - - // possibly merge missing requested tags from dicom-as-json - if (allowStorageAccess && - !resource.missingRequestedTags_.empty() && - !DicomMap::HasOnlyComputedTags(resource.missingRequestedTags_)) - { - OrthancConfiguration::ReaderLock lock; - if (lock.GetConfiguration().IsWarningEnabled(Warnings_001_TagsBeingReadFromStorage)) - { - std::set<DicomTag> missingTags; - Toolbox::AppendSets(missingTags, resource.missingRequestedTags_); - for (std::set<DicomTag>::const_iterator it = resource.missingRequestedTags_.begin(); it != resource.missingRequestedTags_.end(); ++it) - { - if (DicomMap::IsComputedTag(*it)) - { - missingTags.erase(*it); - } - } - - std::string missings; - FromDcmtkBridge::FormatListOfTags(missings, missingTags); - - LOG(WARNING) << "W001: Accessing Dicom tags from storage when accessing " - << Orthanc::GetResourceTypeText(resource.GetLevel(), false, false) - << " : " << missings; - } - - - std::string instanceId_ = instanceId; - DicomMap tagsFromJson; - - if (dicomAsJson == NULL) - { - if (instanceId_.empty()) - { - if (level == ResourceType_Instance) - { - instanceId_ = publicId; - } - else - { - std::list<std::string> instancesIds; - GetIndex().GetChildInstances(instancesIds, publicId); - if (instancesIds.size() < 1) - { - throw OrthancException(ErrorCode_InternalError, "ExpandResource: no instances found"); - } - instanceId_ = instancesIds.front(); - } - } - - Json::Value tmpDicomAsJson; - ReadDicomAsJson(tmpDicomAsJson, instanceId_, resource.missingRequestedTags_ /* ignoreTagLength */); // read all tags from DICOM and avoid cropping requested tags - tagsFromJson.FromDicomAsJson(tmpDicomAsJson, false /* append */, true /* parseSequences*/); - } - else - { - tagsFromJson.FromDicomAsJson(*dicomAsJson, false /* append */, true /* parseSequences*/); - } - - resource.GetMainDicomTags().Merge(tagsFromJson); - } - - // compute the requested tags - ComputeTags(resource, *this, publicId, level, requestedTags); - } - else - { - return false; - } - - return true; - } - int64_t ServerContext::GetServerUpTime() const { boost::posix_time::ptime nowUtc = boost::posix_time::second_clock::universal_time();
--- a/OrthancServer/Sources/ServerContext.h Sat Jul 13 00:58:29 2024 +0200 +++ b/OrthancServer/Sources/ServerContext.h Sat Jul 13 00:59:01 2024 +0200 @@ -66,25 +66,6 @@ friend class ServerIndex; // To access "RemoveFile()" public: - class ILookupVisitor : public boost::noncopyable - { - public: - virtual ~ILookupVisitor() - { - } - - virtual bool IsDicomAsJsonNeeded() const = 0; - - virtual void MarkAsComplete() = 0; - - // NB: "dicomAsJson" must *not* be deleted, and can be NULL if - // "!IsDicomAsJsonNeeded()" - virtual void Visit(const std::string& publicId, - const std::string& instanceId, - const DicomMap& mainDicomTags, - const Json::Value* dicomAsJson) = 0; - }; - struct StoreResult { private: @@ -447,23 +428,6 @@ return (level == ResourceType_Instance ? limitFindInstances_ : limitFindResults_); } - void Apply(ILookupVisitor& visitor, - const DatabaseLookup& lookup, - ResourceType queryLevel, - const std::set<std::string>& labels, - LabelsConstraint labelsConstraint, - size_t since, - size_t limit); - - void Apply(ILookupVisitor& visitor, - const DatabaseLookup& lookup, - ResourceType queryLevel, - size_t since, - size_t limit) - { - Apply(visitor, lookup, queryLevel, std::set<std::string>(), LabelsConstraint_All, since, limit); - } - bool LookupOrReconstructMetadata(std::string& target, const std::string& publicId, ResourceType level, @@ -597,33 +561,6 @@ void SetUnknownSopClassAccepted(bool accepted); - bool ExpandResource(Json::Value& target, - const std::string& publicId, - ResourceType level, - DicomToJsonFormat format, - const std::set<DicomTag>& requestedTags, - bool allowStorageAccess); - - bool ExpandResource(Json::Value& target, - const std::string& publicId, - const DicomMap& mainDicomTags, // optional: the main dicom tags for the resource (if already available) - const std::string& instanceId, // optional: the id of an instance for the resource - const Json::Value* dicomAsJson, // optional: the dicom-as-json for the resource - ResourceType level, - DicomToJsonFormat format, - const std::set<DicomTag>& requestedTags, - bool allowStorageAccess); - - bool ExpandResource(ExpandedResource& target, - const std::string& publicId, - const DicomMap& mainDicomTags, // optional: the main dicom tags for the resource (if already available) - const std::string& instanceId, // optional: the id of an instance for the resource - const Json::Value* dicomAsJson, // optional: the dicom-as-json for the resource - ResourceType level, - const std::set<DicomTag>& requestedTags, - ExpandResourceFlags expandFlags, - bool allowStorageAccess); - FindStorageAccessMode GetFindStorageAccessMode() const { return findStorageAccessMode_;