changeset 5574:5a13483d12c5 find-refactoring

find-refactoring: attachments
author Alain Mazy <am@orthanc.team>
date Fri, 26 Apr 2024 16:32:18 +0200
parents 738f80622e91
children 9e74e761b108
files NEWS OrthancServer/Sources/Database/FindRequest.h OrthancServer/Sources/Database/FindResponse.h OrthancServer/Sources/Database/SQLiteDatabaseWrapper.cpp OrthancServer/Sources/Database/StatelessDatabaseOperations.cpp OrthancServer/Sources/OrthancRestApi/OrthancRestResources.cpp
diffstat 6 files changed, 104 insertions(+), 16 deletions(-) [+]
line wrap: on
line diff
--- a/NEWS	Thu Apr 25 17:07:33 2024 +0200
+++ b/NEWS	Fri Apr 26 16:32:18 2024 +0200
@@ -27,7 +27,7 @@
   in case you just want to update the MainDicomTags of that resource level only 
   e.g. after you have updated the 'ExtraMainDicomTags' for this level.
 * TODO-FIND: complete the list of updated routes:
-  /studies?expand and sibbling routes now also return "Metadata"
+  /studies?expand and sibbling routes now also return "Metadata" (if the DB implements 'extended-api-v1')
 
 Plugins
 -------
--- a/OrthancServer/Sources/Database/FindRequest.h	Thu Apr 25 17:07:33 2024 +0200
+++ b/OrthancServer/Sources/Database/FindRequest.h	Fri Apr 26 16:32:18 2024 +0200
@@ -49,7 +49,8 @@
       ResponseContent_Parent                = (1 << 4),     // get the id of the parent
       ResponseContent_Children              = (1 << 5),     // retrieve the list of children ids
       ResponseContent_ChildInstanceId       = (1 << 6),     // When you need to access all tags from a patient/study/series, you might need to open the DICOM file of a child instance
-      ResponseContent_IsStable              = (1 << 7),     // This is currently not saved in DB but it could be in the future.
+      ResponseContent_ChildrenMetadata      = (1 << 7),     // That is actually required to compute the series status but could be usefull for other stuffs.
+      ResponseContent_IsStable              = (1 << 8),     // This is currently not saved in DB but it could be in the future.
 
       ResponseContent_IdentifiersOnly       = 0,
       ResponseContent_INTERNAL              = 0x7FFFFFFF
--- a/OrthancServer/Sources/Database/FindResponse.h	Thu Apr 25 17:07:33 2024 +0200
+++ b/OrthancServer/Sources/Database/FindResponse.h	Fri Apr 26 16:32:18 2024 +0200
@@ -23,6 +23,8 @@
 #pragma once
 
 #include "../../../OrthancFramework/Sources/DicomFormat/DicomMap.h"
+#include "../../../OrthancFramework/Sources/Enumerations.h"
+#include "../../../OrthancFramework/Sources/FileStorage/FileInfo.h"
 #include "../ServerEnumerations.h"
 #include "OrthancIdentifiers.h"
 #include "FindRequest.h"
@@ -90,7 +92,7 @@
       std::string                           childInstanceId_;
       std::set<std::string>                 labels_;      
       std::map<MetadataType, std::string>   metadata_;
-      std::map<uint16_t, std::string>       attachments_;
+      std::map<FileContentType, FileInfo>   attachments_;
 
     public:
       Item(FindRequest::ResponseContent responseContent,
@@ -194,6 +196,29 @@
       {
         return labels_;
       }
+
+      void AddAttachment(const FileInfo& attachment)
+      {
+        attachments_[attachment.GetContentType()] = attachment;
+      }
+
+      const std::map<FileContentType, FileInfo>& GetAttachments() const
+      {
+        return attachments_;
+      }
+
+      bool LookupAttachment(FileInfo& target, FileContentType type) const
+      {
+        std::map<FileContentType, FileInfo>::const_iterator it = attachments_.find(type);
+        if (it != attachments_.end())
+        {
+          target = it->second;
+          return true;
+        }
+
+        return false;
+      }
+
       // TODO-FIND: add other getters and setters
     };
 
--- a/OrthancServer/Sources/Database/SQLiteDatabaseWrapper.cpp	Thu Apr 25 17:07:33 2024 +0200
+++ b/OrthancServer/Sources/Database/SQLiteDatabaseWrapper.cpp	Fri Apr 26 16:32:18 2024 +0200
@@ -1285,7 +1285,29 @@
             }
           }
 
-          // TODO-FIND: implement other responseContent: ResponseContent_ChildInstanceId, ResponseContent_Attachments, (later: ResponseContent_IsStable)
+          if (request.HasResponseContent(FindRequest::ResponseContent_Attachments))
+          {
+            SQLite::Statement statement(db_, SQLITE_FROM_HERE, 
+                "SELECT filtered.publicId, uuid, fileType, uncompressedSize, compressionType, compressedSize, "
+                "       uncompressedMD5, compressedMD5 "
+                "FROM AttachedFiles "
+                "  INNER JOIN FilteredResourcesIds filtered ON filtered.internalId = AttachedFiles.id");
+
+            while (statement.Step())
+            {
+              const std::string& resourceId = statement.ColumnString(0);
+              FileInfo attachment = FileInfo(statement.ColumnString(1),
+                                             static_cast<FileContentType>(statement.ColumnInt(2)),
+                                             statement.ColumnInt64(3),
+                                             statement.ColumnString(6),
+                                             static_cast<CompressionType>(statement.ColumnInt(4)),
+                                             statement.ColumnInt64(5),
+                                             statement.ColumnString(7));
+              items[resourceId]->AddAttachment(attachment);
+            };
+          }
+
+          // TODO-FIND: implement other responseContent: ResponseContent_ChildInstanceId, ResponseContent_ChildrenMetadata (later: ResponseContent_IsStable)
 
         }
       }
--- a/OrthancServer/Sources/Database/StatelessDatabaseOperations.cpp	Thu Apr 25 17:07:33 2024 +0200
+++ b/OrthancServer/Sources/Database/StatelessDatabaseOperations.cpp	Fri Apr 26 16:32:18 2024 +0200
@@ -3926,6 +3926,24 @@
     {
       labels_ = item.GetLabels();
     }
-    // TODO-FIND: continue: isStable_, satus_, fileSize_, fileUuid_
+
+    if (item.HasResponseContent(FindRequest::ResponseContent_Attachments))
+    {
+      FileInfo attachment;
+      if (item.LookupAttachment(attachment, FileContentType_Dicom))
+      {
+        fileSize_ = attachment.GetUncompressedSize();
+        fileUuid_ = attachment.GetUuid();
+      }
+    }
+
+    if (item.HasResponseContent(FindRequest::ResponseContent_ChildrenMetadata))
+    {
+      // TODO-FIND: the status_ is normally obtained from transaction.GetSeriesStatus(internalId, i)
+      // but, that's an heavy operation for something that is rarely used -> we should have dedicated SQL code 
+      // to compute it AFAP and we should compute it only if the user request it !
+    }
+
+    // TODO-FIND: continue: isStable_, status_
   }
 }
--- a/OrthancServer/Sources/OrthancRestApi/OrthancRestResources.cpp	Thu Apr 25 17:07:33 2024 +0200
+++ b/OrthancServer/Sources/OrthancRestApi/OrthancRestResources.cpp	Fri Apr 26 16:32:18 2024 +0200
@@ -230,20 +230,47 @@
        * 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);
+
+      const DicomToJsonFormat format = OrthancRestApi::GetDicomFormat(call, DicomToJsonFormat_Human);
+
       FindRequest request(resourceType);
 
       if (expand)
       {
-        request.SetResponseContent(static_cast<FindRequest::ResponseContent>(FindRequest::ResponseContent_MainDicomTags |
+        // compatibility with default expand option
+        FindRequest::ResponseContent responseContent = static_cast<FindRequest::ResponseContent>(FindRequest::ResponseContent_MainDicomTags |
                                    FindRequest::ResponseContent_Metadata |
-                                   FindRequest::ResponseContent_Labels |
-                                   FindRequest::ResponseContent_Attachments |
-                                   FindRequest::ResponseContent_Parent |
-                                   FindRequest::ResponseContent_Children));
-
+                                   FindRequest::ResponseContent_Labels);
+        
+        if (requestedTags.size() > 0 && resourceType != ResourceType_Instance) // if we are requesting specific tags that might be outside of the MainDicomTags, we must get a childInstanceId too
+        {
+          responseContent = static_cast<FindRequest::ResponseContent>(responseContent | FindRequest::ResponseContent_ChildInstanceId);
+        }
+        if (resourceType == ResourceType_Series)
+        {
+          responseContent = static_cast<FindRequest::ResponseContent>(responseContent | FindRequest::ResponseContent_ChildrenMetadata); // required for the SeriesStatus
+        }
+        if (resourceType != ResourceType_Instance)
+        {
+          responseContent = static_cast<FindRequest::ResponseContent>(responseContent | FindRequest::ResponseContent_Children);
+        }
+        if (resourceType == ResourceType_Instance)
+        {
+          responseContent = static_cast<FindRequest::ResponseContent>(responseContent | FindRequest::ResponseContent_Attachments); // for FileSize & FileUuid
+        }
+        if (resourceType != ResourceType_Patient)
+        {
+          responseContent = static_cast<FindRequest::ResponseContent>(responseContent | FindRequest::ResponseContent_Parent);
+        }
+
+        request.SetResponseContent(responseContent);
         request.SetRetrieveTagsAtLevel(resourceType, true);
 
         if (resourceType == ResourceType_Study)
@@ -294,11 +321,6 @@
       }
       else
       {
-        std::set<DicomTag> requestedTags;
-        OrthancRestApi::GetRequestedTags(requestedTags, call);
-
-        const DicomToJsonFormat format = OrthancRestApi::GetDicomFormat(call, DicomToJsonFormat_Human);
-
         for (size_t i = 0; i < response.GetSize(); i++)
         {
           context.AppendFindResponse(answer, response.GetItem(i), format, requestedTags, true /* allowStorageAccess */);