comparison 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
comparison
equal deleted inserted replaced
4692:e68edf92e5cc 4693:45bce660ce3a
144 } 144 }
145 145
146 if (call.ParseJsonRequest(request) && 146 if (call.ParseJsonRequest(request) &&
147 request.isObject()) 147 request.isObject())
148 { 148 {
149 bool patientNameReplaced; 149 bool patientNameOverridden;
150 target.ParseAnonymizationRequest(patientNameReplaced, request); 150 target.ParseAnonymizationRequest(patientNameOverridden, request);
151 151
152 if (patientNameReplaced) 152 if (!patientNameOverridden)
153 { 153 {
154 // Overwrite the random Patient's Name by one that is more 154 // Override the random Patient's Name by one that is more
155 // user-friendly (provided none was specified by the user) 155 // user-friendly (provided none was specified by the user)
156 target.Replace(DICOM_TAG_PATIENT_NAME, GeneratePatientName(OrthancRestApi::GetContext(call)), true); 156 target.Replace(DICOM_TAG_PATIENT_NAME, GeneratePatientName(OrthancRestApi::GetContext(call)), true);
157 } 157 }
158 } 158 }
159 else 159 else
203 } 203 }
204 } 204 }
205 else 205 else
206 { 206 {
207 modified->Answer(call.GetOutput()); 207 modified->Answer(call.GetOutput());
208 }
209 }
210
211
212 static ResourceType DetectModifyLevel(const DicomModification& modification)
213 {
214 if (modification.IsReplaced(DICOM_TAG_PATIENT_ID))
215 {
216 return ResourceType_Patient;
217 }
218 else if (modification.IsReplaced(DICOM_TAG_STUDY_INSTANCE_UID))
219 {
220 return ResourceType_Study;
221 }
222 else if (modification.IsReplaced(DICOM_TAG_SERIES_INSTANCE_UID))
223 {
224 return ResourceType_Series;
225 }
226 else
227 {
228 return ResourceType_Instance;
208 } 229 }
209 } 230 }
210 231
211 232
212 static void ModifyInstance(RestApiPostCall& call) 233 static void ModifyInstance(RestApiPostCall& call)
228 modification.SetAllowManualIdentifiers(true); 249 modification.SetAllowManualIdentifiers(true);
229 250
230 Json::Value request; 251 Json::Value request;
231 ParseModifyRequest(request, modification, call); 252 ParseModifyRequest(request, modification, call);
232 253
233 if (modification.IsReplaced(DICOM_TAG_PATIENT_ID)) 254 modification.SetLevel(DetectModifyLevel(modification));
234 {
235 modification.SetLevel(ResourceType_Patient);
236 }
237 else if (modification.IsReplaced(DICOM_TAG_STUDY_INSTANCE_UID))
238 {
239 modification.SetLevel(ResourceType_Study);
240 }
241 else if (modification.IsReplaced(DICOM_TAG_SERIES_INSTANCE_UID))
242 {
243 modification.SetLevel(ResourceType_Series);
244 }
245 else
246 {
247 modification.SetLevel(ResourceType_Instance);
248 }
249 255
250 static const char* TRANSCODE = "Transcode"; 256 static const char* TRANSCODE = "Transcode";
251 if (request.isMember(TRANSCODE)) 257 if (request.isMember(TRANSCODE))
252 { 258 {
253 std::string s = SerializationToolbox::ReadString(request, TRANSCODE); 259 std::string s = SerializationToolbox::ReadString(request, TRANSCODE);
309 315
310 static void SubmitModificationJob(std::unique_ptr<DicomModification>& modification, 316 static void SubmitModificationJob(std::unique_ptr<DicomModification>& modification,
311 bool isAnonymization, 317 bool isAnonymization,
312 RestApiPostCall& call, 318 RestApiPostCall& call,
313 const Json::Value& body, 319 const Json::Value& body,
314 ResourceType level) 320 ResourceType outputLevel /* unused for multiple resources */,
321 bool isSingleResource,
322 const std::set<std::string>& resources)
315 { 323 {
316 ServerContext& context = OrthancRestApi::GetContext(call); 324 ServerContext& context = OrthancRestApi::GetContext(call);
317 325
318 std::unique_ptr<ResourceModificationJob> job(new ResourceModificationJob(context)); 326 std::unique_ptr<ResourceModificationJob> job(new ResourceModificationJob(context));
319 327
320 job->SetModification(modification.release(), level, isAnonymization); 328 if (isSingleResource) // This notably configures the output format
329 {
330 job->SetSingleResourceModification(modification.release(), outputLevel, isAnonymization);
331 }
332 else
333 {
334 job->SetMultipleResourcesModification(modification.release(), isAnonymization);
335 }
336
321 job->SetOrigin(call); 337 job->SetOrigin(call);
322 SetKeepSource(*job, body); 338 SetKeepSource(*job, body);
323 339
324 static const char* TRANSCODE = "Transcode"; 340 static const char* TRANSCODE = "Transcode";
325 if (body.isMember(TRANSCODE)) 341 if (body.isMember(TRANSCODE))
326 { 342 {
327 job->SetTranscode(SerializationToolbox::ReadString(body, TRANSCODE)); 343 job->SetTranscode(SerializationToolbox::ReadString(body, TRANSCODE));
328 } 344 }
345
346 for (std::set<std::string>::const_iterator
347 it = resources.begin(); it != resources.end(); ++it)
348 {
349 context.AddChildInstances(*job, *it);
350 }
329 351
330 context.AddChildInstances(*job, call.GetUriComponent("id", ""));
331 job->AddTrailingStep(); 352 job->AddTrailingStep();
332 353
333 OrthancRestApi::GetApi(call).SubmitCommandsJob 354 OrthancRestApi::GetApi(call).SubmitCommandsJob
334 (call, job.release(), true /* synchronous by default */, body); 355 (call, job.release(), true /* synchronous by default */, body);
356 }
357
358
359 static void SubmitModificationJob(std::unique_ptr<DicomModification>& modification,
360 bool isAnonymization,
361 RestApiPostCall& call,
362 const Json::Value& body,
363 ResourceType outputLevel)
364 {
365 // This was the only flavor in Orthanc <= 1.9.3
366 std::set<std::string> resources;
367 resources.insert(call.GetUriComponent("id", ""));
368
369 SubmitModificationJob(modification, isAnonymization, call, body, outputLevel,
370 true /* single resource */, resources);
371 }
372
373
374 static void SubmitBulkJob(std::unique_ptr<DicomModification>& modification,
375 bool isAnonymization,
376 RestApiPostCall& call,
377 const Json::Value& body)
378 {
379 std::set<std::string> resources;
380 SerializationToolbox::ReadSetOfStrings(resources, body, "Resources");
381
382 SubmitModificationJob(modification, isAnonymization,
383 call, body, ResourceType_Instance /* arbitrary value, unused */,
384 false /* multiple resources */, resources);
335 } 385 }
336 386
337 387
338 template <enum ResourceType resourceType> 388 template <enum ResourceType resourceType>
339 static void ModifyResource(RestApiPostCall& call) 389 static void ModifyResource(RestApiPostCall& call)
358 408
359 Json::Value body; 409 Json::Value body;
360 ParseModifyRequest(body, *modification, call); 410 ParseModifyRequest(body, *modification, call);
361 411
362 modification->SetLevel(resourceType); 412 modification->SetLevel(resourceType);
363 413
364 SubmitModificationJob(modification, false /* not an anonymization */, 414 SubmitModificationJob(modification, false /* not an anonymization */,
365 call, body, resourceType); 415 call, body, resourceType);
416 }
417
418
419 // New in Orthanc 1.9.4
420 static void BulkModify(RestApiPostCall& call)
421 {
422 if (call.IsDocumentation())
423 {
424 OrthancRestApi::DocumentSubmitCommandsJob(call);
425 DocumentModifyOptions(call);
426 call.GetDocumentation()
427 .SetTag("System")
428 .SetSummary("Modify a set of instances")
429 .SetRequestField("Resources", RestApiCallDocumentation::Type_JsonListOfStrings,
430 "List of the Orthanc identifiers of the patients/studies/series/instances of interest.", false)
431 .SetDescription("Start a job that will modify all the DICOM patients, studies, series or instances "
432 "whose identifiers are provided in the `Resources` field.")
433 .AddAnswerType(MimeType_Json, "The list of all the resources that have been altered by this modification");
434 return;
435 }
436
437 std::unique_ptr<DicomModification> modification(new DicomModification);
438
439 Json::Value body;
440 ParseModifyRequest(body, *modification, call);
441
442 modification->SetLevel(DetectModifyLevel(*modification));
443
444 SubmitBulkJob(modification, false /* not an anonymization */, call, body);
366 } 445 }
367 446
368 447
369 template <enum ResourceType resourceType> 448 template <enum ResourceType resourceType>
370 static void AnonymizeResource(RestApiPostCall& call) 449 static void AnonymizeResource(RestApiPostCall& call)
390 Json::Value body; 469 Json::Value body;
391 ParseAnonymizationRequest(body, *modification, call); 470 ParseAnonymizationRequest(body, *modification, call);
392 471
393 SubmitModificationJob(modification, true /* anonymization */, 472 SubmitModificationJob(modification, true /* anonymization */,
394 call, body, resourceType); 473 call, body, resourceType);
474 }
475
476
477 // New in Orthanc 1.9.4
478 static void BulkAnonymize(RestApiPostCall& call)
479 {
480 if (call.IsDocumentation())
481 {
482 OrthancRestApi::DocumentSubmitCommandsJob(call);
483 DocumentAnonymizationOptions(call);
484 call.GetDocumentation()
485 .SetTag("System")
486 .SetSummary("Anonymize a set of instances")
487 .SetRequestField("Resources", RestApiCallDocumentation::Type_JsonListOfStrings,
488 "List of the Orthanc identifiers of the patients/studies/series/instances of interest.", false)
489 .SetDescription("Start a job that will anonymize all the DICOM patients, studies, series or instances "
490 "whose identifiers are provided in the `Resources` field.")
491 .AddAnswerType(MimeType_Json, "The list of all the resources that have been created by this anonymization");
492 return;
493 }
494
495 std::unique_ptr<DicomModification> modification(new DicomModification);
496
497 Json::Value body;
498 ParseAnonymizationRequest(body, *modification, call);
499
500 SubmitBulkJob(modification, true /* anonymization */, call, body);
395 } 501 }
396 502
397 503
398 static void StoreCreatedInstance(std::string& id /* out */, 504 static void StoreCreatedInstance(std::string& id /* out */,
399 RestApiPostCall& call, 505 RestApiPostCall& call,
1052 { 1158 {
1053 Register("/instances/{id}/modify", ModifyInstance); 1159 Register("/instances/{id}/modify", ModifyInstance);
1054 Register("/series/{id}/modify", ModifyResource<ResourceType_Series>); 1160 Register("/series/{id}/modify", ModifyResource<ResourceType_Series>);
1055 Register("/studies/{id}/modify", ModifyResource<ResourceType_Study>); 1161 Register("/studies/{id}/modify", ModifyResource<ResourceType_Study>);
1056 Register("/patients/{id}/modify", ModifyResource<ResourceType_Patient>); 1162 Register("/patients/{id}/modify", ModifyResource<ResourceType_Patient>);
1163 Register("/tools/bulk-modify", BulkModify);
1057 1164
1058 Register("/instances/{id}/anonymize", AnonymizeInstance); 1165 Register("/instances/{id}/anonymize", AnonymizeInstance);
1059 Register("/series/{id}/anonymize", AnonymizeResource<ResourceType_Series>); 1166 Register("/series/{id}/anonymize", AnonymizeResource<ResourceType_Series>);
1060 Register("/studies/{id}/anonymize", AnonymizeResource<ResourceType_Study>); 1167 Register("/studies/{id}/anonymize", AnonymizeResource<ResourceType_Study>);
1061 Register("/patients/{id}/anonymize", AnonymizeResource<ResourceType_Patient>); 1168 Register("/patients/{id}/anonymize", AnonymizeResource<ResourceType_Patient>);
1169 Register("/tools/bulk-anonymize", BulkAnonymize);
1062 1170
1063 Register("/tools/create-dicom", CreateDicom); 1171 Register("/tools/create-dicom", CreateDicom);
1064 1172
1065 Register("/studies/{id}/split", SplitStudy); 1173 Register("/studies/{id}/split", SplitStudy);
1066 Register("/studies/{id}/merge", MergeStudy); 1174 Register("/studies/{id}/merge", MergeStudy);