# HG changeset patch # User Alain Mazy # Date 1637655659 -3600 # Node ID 2ca4213fb50acc3b53b58b59eadf4fee35b6fed4 # Parent 7e1740813fa44d81122d8a5aec30ca09735000a2# Parent 0bd98c52474b593ab7a0ff950436e6b977a9e649 merged ReceivedCStoreInstanceFilter diff -r 0bd98c52474b -r 2ca4213fb50a NEWS --- a/NEWS Fri Oct 15 18:31:20 2021 +0200 +++ b/NEWS Tue Nov 23 09:20:59 2021 +0100 @@ -1,12 +1,28 @@ Pending changes in the mainline =============================== - * Fix handling of option "DeidentifyLogs", notably for tags (0010,0010) and (0010,0020) * New configuration options: - "DicomThreadsCount" to set the number of threads in the embedded DICOM server +REST API +-------- + +* API version upgraded to 16 +* If an image can not be decoded, ../preview and ../rendered routes are now returning + unsupported.png only if the ?returnUnsupportedImage option is specified; otherwise, + it raises a 415 error code. +* Archive jobs response now contains a header Content-Disposition:filename='archive.zip' + + + +Maintenance +----------- + +* Fix instances accumulating in DB while their attachments were not stored because of + MaximumStorageSize limit reached with a single patient in DB. + Lua --- @@ -19,6 +35,7 @@ * New function in the SDK: OrthancPluginRegisterIncomingCStoreInstanceFilter() + Version 1.9.7 (2021-08-31) ========================== diff -r 0bd98c52474b -r 2ca4213fb50a OrthancFramework/Resources/CMake/OrthancFrameworkParameters.cmake --- a/OrthancFramework/Resources/CMake/OrthancFrameworkParameters.cmake Fri Oct 15 18:31:20 2021 +0200 +++ b/OrthancFramework/Resources/CMake/OrthancFrameworkParameters.cmake Tue Nov 23 09:20:59 2021 +0100 @@ -37,7 +37,7 @@ # Version of the Orthanc API, can be retrieved from "/system" URI in # order to check whether new URI endpoints are available even if using # the mainline version of Orthanc -set(ORTHANC_API_VERSION "15") +set(ORTHANC_API_VERSION "16") ##################################################################### diff -r 0bd98c52474b -r 2ca4213fb50a OrthancFramework/Sources/Compression/ZipReader.cpp --- a/OrthancFramework/Sources/Compression/ZipReader.cpp Fri Oct 15 18:31:20 2021 +0200 +++ b/OrthancFramework/Sources/Compression/ZipReader.cpp Tue Nov 23 09:20:59 2021 +0100 @@ -353,7 +353,7 @@ } else { - throw OrthancException(ErrorCode_BadFileFormat); + throw OrthancException(ErrorCode_BadFileFormat, "Invalid file or unsupported compression method (e.g. Deflate64)"); } } diff -r 0bd98c52474b -r 2ca4213fb50a OrthancFramework/Sources/JobsEngine/IJob.h --- a/OrthancFramework/Sources/JobsEngine/IJob.h Fri Oct 15 18:31:20 2021 +0200 +++ b/OrthancFramework/Sources/JobsEngine/IJob.h Tue Nov 23 09:20:59 2021 +0100 @@ -59,6 +59,7 @@ // "success" state virtual bool GetOutput(std::string& output, MimeType& mime, + std::string& filename, const std::string& key) = 0; }; } diff -r 0bd98c52474b -r 2ca4213fb50a OrthancFramework/Sources/JobsEngine/JobsRegistry.cpp --- a/OrthancFramework/Sources/JobsEngine/JobsRegistry.cpp Fri Oct 15 18:31:20 2021 +0200 +++ b/OrthancFramework/Sources/JobsEngine/JobsRegistry.cpp Tue Nov 23 09:20:59 2021 +0100 @@ -650,6 +650,7 @@ bool JobsRegistry::GetJobOutput(std::string& output, MimeType& mime, + std::string& filename, const std::string& job, const std::string& key) { @@ -668,7 +669,7 @@ if (handler.GetState() == JobState_Success) { - return handler.GetJob().GetOutput(output, mime, key); + return handler.GetJob().GetOutput(output, mime, filename, key); } else { diff -r 0bd98c52474b -r 2ca4213fb50a OrthancFramework/Sources/JobsEngine/JobsRegistry.h --- a/OrthancFramework/Sources/JobsEngine/JobsRegistry.h Fri Oct 15 18:31:20 2021 +0200 +++ b/OrthancFramework/Sources/JobsEngine/JobsRegistry.h Tue Nov 23 09:20:59 2021 +0100 @@ -148,6 +148,7 @@ bool GetJobOutput(std::string& output, MimeType& mime, + std::string& filename, const std::string& job, const std::string& key); diff -r 0bd98c52474b -r 2ca4213fb50a OrthancFramework/Sources/JobsEngine/Operations/SequenceOfOperationsJob.cpp --- a/OrthancFramework/Sources/JobsEngine/Operations/SequenceOfOperationsJob.cpp Fri Oct 15 18:31:20 2021 +0200 +++ b/OrthancFramework/Sources/JobsEngine/Operations/SequenceOfOperationsJob.cpp Tue Nov 23 09:20:59 2021 +0100 @@ -448,6 +448,7 @@ bool SequenceOfOperationsJob::GetOutput(std::string& output, MimeType& mime, + std::string& filename, const std::string& key) { return false; diff -r 0bd98c52474b -r 2ca4213fb50a OrthancFramework/Sources/JobsEngine/Operations/SequenceOfOperationsJob.h --- a/OrthancFramework/Sources/JobsEngine/Operations/SequenceOfOperationsJob.h Fri Oct 15 18:31:20 2021 +0200 +++ b/OrthancFramework/Sources/JobsEngine/Operations/SequenceOfOperationsJob.h Tue Nov 23 09:20:59 2021 +0100 @@ -125,6 +125,7 @@ virtual bool GetOutput(std::string& output, MimeType& mime, + std::string& filename, const std::string& key) ORTHANC_OVERRIDE; void AwakeTrailingSleep(); diff -r 0bd98c52474b -r 2ca4213fb50a OrthancFramework/Sources/JobsEngine/SetOfCommandsJob.cpp --- a/OrthancFramework/Sources/JobsEngine/SetOfCommandsJob.cpp Fri Oct 15 18:31:20 2021 +0200 +++ b/OrthancFramework/Sources/JobsEngine/SetOfCommandsJob.cpp Tue Nov 23 09:20:59 2021 +0100 @@ -270,6 +270,7 @@ bool SetOfCommandsJob::GetOutput(std::string &output, MimeType &mime, + std::string& filename, const std::string &key) { return false; diff -r 0bd98c52474b -r 2ca4213fb50a OrthancFramework/Sources/JobsEngine/SetOfCommandsJob.h --- a/OrthancFramework/Sources/JobsEngine/SetOfCommandsJob.h Fri Oct 15 18:31:20 2021 +0200 +++ b/OrthancFramework/Sources/JobsEngine/SetOfCommandsJob.h Tue Nov 23 09:20:59 2021 +0100 @@ -104,6 +104,7 @@ virtual bool GetOutput(std::string& output, MimeType& mime, + std::string& filename, const std::string& key) ORTHANC_OVERRIDE; }; } diff -r 0bd98c52474b -r 2ca4213fb50a OrthancFramework/Sources/RestApi/RestApiOutput.cpp --- a/OrthancFramework/Sources/RestApi/RestApiOutput.cpp Fri Oct 15 18:31:20 2021 +0200 +++ b/OrthancFramework/Sources/RestApi/RestApiOutput.cpp Tue Nov 23 09:20:59 2021 +0100 @@ -214,4 +214,9 @@ // empty string SetCookie(name, "", 1); } + + void RestApiOutput::SetContentFilename(const char* filename) + { + output_.SetContentFilename(filename); + } } diff -r 0bd98c52474b -r 2ca4213fb50a OrthancFramework/Sources/RestApi/RestApiOutput.h --- a/OrthancFramework/Sources/RestApi/RestApiOutput.h Fri Oct 15 18:31:20 2021 +0200 +++ b/OrthancFramework/Sources/RestApi/RestApiOutput.h Tue Nov 23 09:20:59 2021 +0100 @@ -77,6 +77,8 @@ size_t length, MimeType contentType); + void SetContentFilename(const char* filename); + void SignalError(HttpStatus status); void SignalError(HttpStatus status, diff -r 0bd98c52474b -r 2ca4213fb50a OrthancFramework/UnitTestsSources/JobsTests.cpp --- a/OrthancFramework/UnitTestsSources/JobsTests.cpp Fri Oct 15 18:31:20 2021 +0200 +++ b/OrthancFramework/UnitTestsSources/JobsTests.cpp Tue Nov 23 09:20:59 2021 +0100 @@ -125,6 +125,7 @@ virtual bool GetOutput(std::string& output, MimeType& mime, + std::string& filename, const std::string& key) ORTHANC_OVERRIDE { return false; diff -r 0bd98c52474b -r 2ca4213fb50a OrthancServer/OrthancExplorer/explorer.js --- a/OrthancServer/OrthancExplorer/explorer.js Fri Oct 15 18:31:20 2021 +0200 +++ b/OrthancServer/OrthancExplorer/explorer.js Tue Nov 23 09:20:59 2021 +0100 @@ -1113,7 +1113,7 @@ if (frames.length == 1) { // Viewing a single-frame image - jQuery.slimbox('../instances/' + pageData.uuid + '/preview', '', { + jQuery.slimbox('../instances/' + pageData.uuid + '/preview?returnUnsupportedImage', '', { overlayFadeDuration : 1, resizeDuration : 1, imageFadeDuration : 1 @@ -1125,7 +1125,7 @@ images = []; for (var i = 0; i < frames.length; i++) { - images.push([ '../instances/' + pageData.uuid + '/frames/' + i + '/preview' ]); + images.push([ '../instances/' + pageData.uuid + '/frames/' + i + '/preview?returnUnsupportedImage' ]); } jQuery.slimbox(images, 0, { @@ -1155,7 +1155,7 @@ images = []; for (var i = 0; i < instances.length; i++) { - images.push([ '../instances/' + instances[i].ID + '/preview', + images.push([ '../instances/' + instances[i].ID + '/preview?returnUnsupportedImage', (i + 1).toString() + '/' + instances.length.toString() ]) } diff -r 0bd98c52474b -r 2ca4213fb50a OrthancServer/Plugins/Engine/PluginsJob.h --- a/OrthancServer/Plugins/Engine/PluginsJob.h Fri Oct 15 18:31:20 2021 +0200 +++ b/OrthancServer/Plugins/Engine/PluginsJob.h Tue Nov 23 09:20:59 2021 +0100 @@ -75,6 +75,7 @@ virtual bool GetOutput(std::string& output, MimeType& mime, + std::string& filename, const std::string& key) ORTHANC_OVERRIDE { // TODO diff -r 0bd98c52474b -r 2ca4213fb50a OrthancServer/Sources/Database/StatelessDatabaseOperations.cpp --- a/OrthancServer/Sources/Database/StatelessDatabaseOperations.cpp Fri Oct 15 18:31:20 2021 +0200 +++ b/OrthancServer/Sources/Database/StatelessDatabaseOperations.cpp Tue Nov 23 09:20:59 2021 +0100 @@ -2810,7 +2810,7 @@ if (!ok) { - throw OrthancException(ErrorCode_FullStorage); + throw OrthancException(ErrorCode_FullStorage, "Cannot recycle more patients"); } LOG(TRACE) << "Recycling one patient"; @@ -3264,11 +3264,18 @@ { if (e.GetErrorCode() == ErrorCode_DatabaseCannotSerialize) { - throw; + throw; // the transaction has failed -> do not commit the current transaction (and retry) } else { - LOG(ERROR) << "EXCEPTION [" << e.What() << "]"; + LOG(ERROR) << "EXCEPTION [" << e.What() << " - " << e.GetDetails() << "]"; + + if (e.GetErrorCode() == ErrorCode_FullStorage) + { + throw; // do not commit the current transaction + } + + // this is an expected failure, exit normaly and commit the current transaction storeStatus_ = StoreStatus_Failure; } } diff -r 0bd98c52474b -r 2ca4213fb50a OrthancServer/Sources/OrthancGetRequestHandler.cpp --- a/OrthancServer/Sources/OrthancGetRequestHandler.cpp Fri Oct 15 18:31:20 2021 +0200 +++ b/OrthancServer/Sources/OrthancGetRequestHandler.cpp Tue Nov 23 09:20:59 2021 +0100 @@ -497,7 +497,7 @@ { MetricsRegistry::Timer timer(context_.GetMetricsRegistry(), "orthanc_get_scp_duration_ms"); - CLOG(WARNING, DICOM) << "C-GET-SCU request received from AET \"" << originatorAet << "\""; + CLOG(INFO, DICOM) << "C-GET-SCU request received from AET \"" << originatorAet << "\""; { DicomArray query(input); diff -r 0bd98c52474b -r 2ca4213fb50a OrthancServer/Sources/OrthancMoveRequestHandler.cpp --- a/OrthancServer/Sources/OrthancMoveRequestHandler.cpp Fri Oct 15 18:31:20 2021 +0200 +++ b/OrthancServer/Sources/OrthancMoveRequestHandler.cpp Tue Nov 23 09:20:59 2021 +0100 @@ -334,7 +334,7 @@ { MetricsRegistry::Timer timer(context_.GetMetricsRegistry(), "orthanc_move_scp_duration_ms"); - CLOG(WARNING, DICOM) << "Move-SCU request received for AET \"" << targetAet << "\""; + CLOG(INFO, DICOM) << "Move-SCU request received for AET \"" << targetAet << "\""; { DicomArray query(input); diff -r 0bd98c52474b -r 2ca4213fb50a OrthancServer/Sources/OrthancRestApi/OrthancRestResources.cpp --- a/OrthancServer/Sources/OrthancRestApi/OrthancRestResources.cpp Fri Oct 15 18:31:20 2021 +0200 +++ b/OrthancServer/Sources/OrthancRestApi/OrthancRestResources.cpp Tue Nov 23 09:20:59 2021 +0100 @@ -755,6 +755,7 @@ .SetTag("Instances") .SetUriArgument("id", "Orthanc identifier of the DICOM instance of interest") .SetHttpGetArgument("quality", RestApiCallDocumentation::Type_Number, "Quality for JPEG images (between 1 and 100, defaults to 90)", false) + .SetHttpGetArgument("returnUnsupportedImage", RestApiCallDocumentation::Type_Boolean, "Returns an unsupported.png placeholder image if unable to provide the image instead of returning a 415 HTTP error (defaults to false)", false) .SetHttpHeader("Accept", "Format of the resulting image. Can be `image/png` (default), `image/jpeg` or `image/x-portable-arbitrarymap`") .AddAnswerType(MimeType_Png, "PNG image") .AddAnswerType(MimeType_Jpeg, "JPEG image") @@ -817,13 +818,20 @@ } else { - std::string root = ""; - for (size_t i = 1; i < call.GetFullUri().size(); i++) + if (call.HasArgument("returnUnsupportedImage")) { - root += "../"; + std::string root = ""; + for (size_t i = 1; i < call.GetFullUri().size(); i++) + { + root += "../"; + } + + call.GetOutput().Redirect(root + "app/images/unsupported.png"); } - - call.GetOutput().Redirect(root + "app/images/unsupported.png"); + else + { + call.GetOutput().SignalError(HttpStatus_415_UnsupportedMediaType); + } } return; } diff -r 0bd98c52474b -r 2ca4213fb50a OrthancServer/Sources/OrthancRestApi/OrthancRestSystem.cpp --- a/OrthancServer/Sources/OrthancRestApi/OrthancRestSystem.cpp Fri Oct 15 18:31:20 2021 +0200 +++ b/OrthancServer/Sources/OrthancRestApi/OrthancRestSystem.cpp Tue Nov 23 09:20:59 2021 +0100 @@ -696,10 +696,16 @@ std::string value; MimeType mime; + std::string filename; if (OrthancRestApi::GetContext(call).GetJobsEngine(). - GetRegistry().GetJobOutput(value, mime, job, key)) + GetRegistry().GetJobOutput(value, mime, filename, job, key)) { + if (!filename.empty()) + { + call.GetOutput().SetContentFilename(filename.c_str()); + } + call.GetOutput().AnswerBuffer(value, mime); } else diff -r 0bd98c52474b -r 2ca4213fb50a OrthancServer/Sources/ServerJobs/ArchiveJob.cpp --- a/OrthancServer/Sources/ServerJobs/ArchiveJob.cpp Fri Oct 15 18:31:20 2021 +0200 +++ b/OrthancServer/Sources/ServerJobs/ArchiveJob.cpp Tue Nov 23 09:20:59 2021 +0100 @@ -1196,6 +1196,7 @@ bool ArchiveJob::GetOutput(std::string& output, MimeType& mime, + std::string& filename, const std::string& key) { if (key == "archive" && @@ -1208,6 +1209,7 @@ const DynamicTemporaryFile& f = dynamic_cast(accessor.GetItem()); f.GetFile().Read(output); mime = MimeType_Zip; + filename = "archive.zip"; return true; } else diff -r 0bd98c52474b -r 2ca4213fb50a OrthancServer/Sources/ServerJobs/ArchiveJob.h --- a/OrthancServer/Sources/ServerJobs/ArchiveJob.h Fri Oct 15 18:31:20 2021 +0200 +++ b/OrthancServer/Sources/ServerJobs/ArchiveJob.h Tue Nov 23 09:20:59 2021 +0100 @@ -118,6 +118,7 @@ virtual bool GetOutput(std::string& output, MimeType& mime, + std::string& filename, const std::string& key) ORTHANC_OVERRIDE; }; } diff -r 0bd98c52474b -r 2ca4213fb50a OrthancServer/UnitTestsSources/ServerJobsTests.cpp --- a/OrthancServer/UnitTestsSources/ServerJobsTests.cpp Fri Oct 15 18:31:20 2021 +0200 +++ b/OrthancServer/UnitTestsSources/ServerJobsTests.cpp Tue Nov 23 09:20:59 2021 +0100 @@ -140,6 +140,7 @@ virtual bool GetOutput(std::string& output, MimeType& mime, + std::string& filename, const std::string& key) ORTHANC_OVERRIDE { return false; diff -r 0bd98c52474b -r 2ca4213fb50a TODO --- a/TODO Fri Oct 15 18:31:20 2021 +0200 +++ b/TODO Tue Nov 23 09:20:59 2021 +0100 @@ -60,6 +60,8 @@ image. The SOPClassUID might be used to identify such secondary captures. * Support "/preview" and "/matlab" for LUT color images +* Try to transcode files if a simple decoding fails: + https://groups.google.com/g/orthanc-users/c/b8168-NkAhA/m/Df3j-CO9CgAJ * Add asynchronous mode in "/modalitities/.../move" for C-MOVE SCU: https://groups.google.com/g/orthanc-users/c/G3_jBy4X4NQ/m/8BanTsdMBQAJ * Ranges of DICOM tags for "Keep" and "Remove" in ".../modify" and ".../anonymize": @@ -125,7 +127,10 @@ useful in ServerContext::DecodeDicomInstance() * DicomMap: create a cache to the main DICOM tags index * Check out rapidjson: https://github.com/miloyip/nativejson-benchmark - +* optimize tools/find with ModalitiesInStudies: + https://groups.google.com/g/orthanc-users/c/aN8nqcRd3jw/m/pmc9ylVeAwAJ. + One solution could be: filter first without ModalitiesInStudies and then + cycle through the responses to filter out with ModalitiesInStudies ======== Database