diff OrthancServer/Sources/OrthancRestApi/OrthancRestAnonymizeModify.cpp @ 4693:45bce660ce3a

added routes for bulk anonymization/modification
author Sebastien Jodogne <s.jodogne@gmail.com>
date Wed, 16 Jun 2021 16:44:04 +0200
parents fcd2dc7c8f31
children da1edb7d6332
line wrap: on
line diff
--- a/OrthancServer/Sources/OrthancRestApi/OrthancRestAnonymizeModify.cpp	Fri Jun 11 10:48:28 2021 +0200
+++ b/OrthancServer/Sources/OrthancRestApi/OrthancRestAnonymizeModify.cpp	Wed Jun 16 16:44:04 2021 +0200
@@ -146,12 +146,12 @@
     if (call.ParseJsonRequest(request) &&
         request.isObject())
     {
-      bool patientNameReplaced;
-      target.ParseAnonymizationRequest(patientNameReplaced, request);
+      bool patientNameOverridden;
+      target.ParseAnonymizationRequest(patientNameOverridden, request);
 
-      if (patientNameReplaced)
+      if (!patientNameOverridden)
       {
-        // Overwrite the random Patient's Name by one that is more
+        // Override the random Patient's Name by one that is more
         // user-friendly (provided none was specified by the user)
         target.Replace(DICOM_TAG_PATIENT_NAME, GeneratePatientName(OrthancRestApi::GetContext(call)), true);
       }
@@ -209,6 +209,27 @@
   }
 
 
+  static ResourceType DetectModifyLevel(const DicomModification& modification)
+  {
+    if (modification.IsReplaced(DICOM_TAG_PATIENT_ID))
+    {
+      return ResourceType_Patient;
+    }
+    else if (modification.IsReplaced(DICOM_TAG_STUDY_INSTANCE_UID))
+    {
+      return ResourceType_Study;
+    }
+    else if (modification.IsReplaced(DICOM_TAG_SERIES_INSTANCE_UID))
+    {
+      return ResourceType_Series;
+    }
+    else
+    {
+      return ResourceType_Instance;
+    }
+  }
+
+
   static void ModifyInstance(RestApiPostCall& call)
   {
     if (call.IsDocumentation())
@@ -230,22 +251,7 @@
     Json::Value request;
     ParseModifyRequest(request, modification, call);
 
-    if (modification.IsReplaced(DICOM_TAG_PATIENT_ID))
-    {
-      modification.SetLevel(ResourceType_Patient);
-    }
-    else if (modification.IsReplaced(DICOM_TAG_STUDY_INSTANCE_UID))
-    {
-      modification.SetLevel(ResourceType_Study);
-    }
-    else if (modification.IsReplaced(DICOM_TAG_SERIES_INSTANCE_UID))
-    {
-      modification.SetLevel(ResourceType_Series);
-    }
-    else
-    {
-      modification.SetLevel(ResourceType_Instance);
-    }
+    modification.SetLevel(DetectModifyLevel(modification));
 
     static const char* TRANSCODE = "Transcode";
     if (request.isMember(TRANSCODE))
@@ -311,13 +317,23 @@
                                     bool isAnonymization,
                                     RestApiPostCall& call,
                                     const Json::Value& body,
-                                    ResourceType level)
+                                    ResourceType outputLevel /* unused for multiple resources */,
+                                    bool isSingleResource,
+                                    const std::set<std::string>& resources)
   {
     ServerContext& context = OrthancRestApi::GetContext(call);
 
     std::unique_ptr<ResourceModificationJob> job(new ResourceModificationJob(context));
 
-    job->SetModification(modification.release(), level, isAnonymization);
+    if (isSingleResource)  // This notably configures the output format
+    {
+      job->SetSingleResourceModification(modification.release(), outputLevel, isAnonymization);
+    }
+    else
+    {
+      job->SetMultipleResourcesModification(modification.release(), isAnonymization);
+    }
+    
     job->SetOrigin(call);
     SetKeepSource(*job, body);
 
@@ -326,8 +342,13 @@
     {
       job->SetTranscode(SerializationToolbox::ReadString(body, TRANSCODE));
     }
+
+    for (std::set<std::string>::const_iterator
+           it = resources.begin(); it != resources.end(); ++it)
+    {
+      context.AddChildInstances(*job, *it);
+    }
     
-    context.AddChildInstances(*job, call.GetUriComponent("id", ""));
     job->AddTrailingStep();
 
     OrthancRestApi::GetApi(call).SubmitCommandsJob
@@ -335,6 +356,35 @@
   }
 
 
+  static void SubmitModificationJob(std::unique_ptr<DicomModification>& modification,
+                                    bool isAnonymization,
+                                    RestApiPostCall& call,
+                                    const Json::Value& body,
+                                    ResourceType outputLevel)
+  {
+    // This was the only flavor in Orthanc <= 1.9.3
+    std::set<std::string> resources;
+    resources.insert(call.GetUriComponent("id", ""));
+    
+    SubmitModificationJob(modification, isAnonymization, call, body, outputLevel,
+                          true /* single resource */, resources);
+  }
+
+  
+  static void SubmitBulkJob(std::unique_ptr<DicomModification>& modification,
+                            bool isAnonymization,
+                            RestApiPostCall& call,
+                            const Json::Value& body)
+  {
+    std::set<std::string> resources;
+    SerializationToolbox::ReadSetOfStrings(resources, body, "Resources");
+
+    SubmitModificationJob(modification, isAnonymization,
+                          call, body, ResourceType_Instance /* arbitrary value, unused */,
+                          false /* multiple resources */, resources);
+  }
+
+
   template <enum ResourceType resourceType>
   static void ModifyResource(RestApiPostCall& call)
   {
@@ -360,12 +410,41 @@
     ParseModifyRequest(body, *modification, call);
 
     modification->SetLevel(resourceType);
-
+    
     SubmitModificationJob(modification, false /* not an anonymization */,
                           call, body, resourceType);
   }
 
 
+  // New in Orthanc 1.9.4
+  static void BulkModify(RestApiPostCall& call)
+  {
+    if (call.IsDocumentation())
+    {
+      OrthancRestApi::DocumentSubmitCommandsJob(call);
+      DocumentModifyOptions(call);
+      call.GetDocumentation()
+        .SetTag("System")
+        .SetSummary("Modify a set of instances")
+        .SetRequestField("Resources", RestApiCallDocumentation::Type_JsonListOfStrings,
+                         "List of the Orthanc identifiers of the patients/studies/series/instances of interest.", false)
+        .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");
+      return;
+    }
+    
+    std::unique_ptr<DicomModification> modification(new DicomModification);
+
+    Json::Value body;
+    ParseModifyRequest(body, *modification, call);
+
+    modification->SetLevel(DetectModifyLevel(*modification));
+
+    SubmitBulkJob(modification, false /* not an anonymization */, call, body);
+  }
+
+
   template <enum ResourceType resourceType>
   static void AnonymizeResource(RestApiPostCall& call)
   {
@@ -395,6 +474,33 @@
   }
 
 
+  // New in Orthanc 1.9.4
+  static void BulkAnonymize(RestApiPostCall& call)
+  {
+    if (call.IsDocumentation())
+    {
+      OrthancRestApi::DocumentSubmitCommandsJob(call);
+      DocumentAnonymizationOptions(call);
+      call.GetDocumentation()
+        .SetTag("System")
+        .SetSummary("Anonymize a set of instances")
+        .SetRequestField("Resources", RestApiCallDocumentation::Type_JsonListOfStrings,
+                         "List of the Orthanc identifiers of the patients/studies/series/instances of interest.", false)
+        .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");
+      return;
+    }
+
+    std::unique_ptr<DicomModification> modification(new DicomModification);
+
+    Json::Value body;
+    ParseAnonymizationRequest(body, *modification, call);
+
+    SubmitBulkJob(modification, true /* anonymization */, call, body);
+  }
+
+
   static void StoreCreatedInstance(std::string& id /* out */,
                                    RestApiPostCall& call,
                                    ParsedDicomFile& dicom,
@@ -1054,11 +1160,13 @@
     Register("/series/{id}/modify", ModifyResource<ResourceType_Series>);
     Register("/studies/{id}/modify", ModifyResource<ResourceType_Study>);
     Register("/patients/{id}/modify", ModifyResource<ResourceType_Patient>);
+    Register("/tools/bulk-modify", BulkModify);
 
     Register("/instances/{id}/anonymize", AnonymizeInstance);
     Register("/series/{id}/anonymize", AnonymizeResource<ResourceType_Series>);
     Register("/studies/{id}/anonymize", AnonymizeResource<ResourceType_Study>);
     Register("/patients/{id}/anonymize", AnonymizeResource<ResourceType_Patient>);
+    Register("/tools/bulk-anonymize", BulkAnonymize);
 
     Register("/tools/create-dicom", CreateDicom);