Mercurial > hg > orthanc
comparison OrthancServer/OrthancRestApi/OrthancRestAnonymizeModify.cpp @ 751:5197fd35333c
refactoring of OrthancRestApi
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Mon, 14 Apr 2014 11:28:35 +0200 |
parents | |
children | aebf0071020e |
comparison
equal
deleted
inserted
replaced
750:4afad8cb94fd | 751:5197fd35333c |
---|---|
1 /** | |
2 * Orthanc - A Lightweight, RESTful DICOM Store | |
3 * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, | |
4 * Belgium | |
5 * | |
6 * This program is free software: you can redistribute it and/or | |
7 * modify it under the terms of the GNU General Public License as | |
8 * published by the Free Software Foundation, either version 3 of the | |
9 * License, or (at your option) any later version. | |
10 * | |
11 * In addition, as a special exception, the copyright holders of this | |
12 * program give permission to link the code of its release with the | |
13 * OpenSSL project's "OpenSSL" library (or with modified versions of it | |
14 * that use the same license as the "OpenSSL" library), and distribute | |
15 * the linked executables. You must obey the GNU General Public License | |
16 * in all respects for all of the code used other than "OpenSSL". If you | |
17 * modify file(s) with this exception, you may extend this exception to | |
18 * your version of the file(s), but you are not obligated to do so. If | |
19 * you do not wish to do so, delete this exception statement from your | |
20 * version. If you delete this exception statement from all source files | |
21 * in the program, then also delete it here. | |
22 * | |
23 * This program is distributed in the hope that it will be useful, but | |
24 * WITHOUT ANY WARRANTY; without even the implied warranty of | |
25 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
26 * General Public License for more details. | |
27 * | |
28 * You should have received a copy of the GNU General Public License | |
29 * along with this program. If not, see <http://www.gnu.org/licenses/>. | |
30 **/ | |
31 | |
32 | |
33 #include "OrthancRestApi.h" | |
34 | |
35 #include <glog/logging.h> | |
36 | |
37 namespace Orthanc | |
38 { | |
39 // TODO IMPROVE MULTITHREADING | |
40 // Every call to "ParsedDicomFile" must lock this mutex!!! | |
41 static boost::mutex cacheMutex_; | |
42 | |
43 | |
44 // Raw access to the DICOM tags of an instance ------------------------------ | |
45 | |
46 static void GetRawContent(RestApi::GetCall& call) | |
47 { | |
48 boost::mutex::scoped_lock lock(cacheMutex_); | |
49 | |
50 ServerContext& context = OrthancRestApi::GetContext(call); | |
51 | |
52 std::string id = call.GetUriComponent("id", ""); | |
53 ParsedDicomFile& dicom = context.GetDicomFile(id); | |
54 dicom.SendPathValue(call.GetOutput(), call.GetTrailingUri()); | |
55 } | |
56 | |
57 | |
58 | |
59 // Modification of DICOM instances ------------------------------------------ | |
60 | |
61 namespace | |
62 { | |
63 typedef std::set<DicomTag> Removals; | |
64 typedef std::map<DicomTag, std::string> Replacements; | |
65 typedef std::map< std::pair<DicomRootLevel, std::string>, std::string> UidMap; | |
66 } | |
67 | |
68 static void ReplaceInstanceInternal(ParsedDicomFile& toModify, | |
69 const Removals& removals, | |
70 const Replacements& replacements, | |
71 DicomReplaceMode mode, | |
72 bool removePrivateTags) | |
73 { | |
74 if (removePrivateTags) | |
75 { | |
76 toModify.RemovePrivateTags(); | |
77 } | |
78 | |
79 for (Removals::const_iterator it = removals.begin(); | |
80 it != removals.end(); ++it) | |
81 { | |
82 toModify.Remove(*it); | |
83 } | |
84 | |
85 for (Replacements::const_iterator it = replacements.begin(); | |
86 it != replacements.end(); ++it) | |
87 { | |
88 toModify.Replace(it->first, it->second, mode); | |
89 } | |
90 | |
91 // A new SOP instance UID is automatically generated | |
92 std::string instanceUid = FromDcmtkBridge::GenerateUniqueIdentifier(DicomRootLevel_Instance); | |
93 toModify.Replace(DICOM_TAG_SOP_INSTANCE_UID, instanceUid, DicomReplaceMode_InsertIfAbsent); | |
94 } | |
95 | |
96 | |
97 static void ParseRemovals(Removals& target, | |
98 const Json::Value& removals) | |
99 { | |
100 if (!removals.isArray()) | |
101 { | |
102 throw OrthancException(ErrorCode_BadRequest); | |
103 } | |
104 | |
105 for (Json::Value::ArrayIndex i = 0; i < removals.size(); i++) | |
106 { | |
107 std::string name = removals[i].asString(); | |
108 DicomTag tag = FromDcmtkBridge::ParseTag(name); | |
109 target.insert(tag); | |
110 | |
111 VLOG(1) << "Removal: " << name << " " << tag << std::endl; | |
112 } | |
113 } | |
114 | |
115 | |
116 static void ParseReplacements(Replacements& target, | |
117 const Json::Value& replacements) | |
118 { | |
119 if (!replacements.isObject()) | |
120 { | |
121 throw OrthancException(ErrorCode_BadRequest); | |
122 } | |
123 | |
124 Json::Value::Members members = replacements.getMemberNames(); | |
125 for (size_t i = 0; i < members.size(); i++) | |
126 { | |
127 const std::string& name = members[i]; | |
128 std::string value = replacements[name].asString(); | |
129 | |
130 DicomTag tag = FromDcmtkBridge::ParseTag(name); | |
131 target[tag] = value; | |
132 | |
133 VLOG(1) << "Replacement: " << name << " " << tag << " == " << value << std::endl; | |
134 } | |
135 } | |
136 | |
137 | |
138 static std::string GeneratePatientName(ServerContext& context) | |
139 { | |
140 uint64_t seq = context.GetIndex().IncrementGlobalSequence(GlobalProperty_AnonymizationSequence); | |
141 return "Anonymized" + boost::lexical_cast<std::string>(seq); | |
142 } | |
143 | |
144 | |
145 static void SetupAnonymization(Removals& removals, | |
146 Replacements& replacements) | |
147 { | |
148 // This is Table E.1-1 from PS 3.15-2008 - DICOM Part 15: Security and System Management Profiles | |
149 removals.insert(DicomTag(0x0008, 0x0014)); // Instance Creator UID | |
150 //removals.insert(DicomTag(0x0008, 0x0018)); // SOP Instance UID => set by ReplaceInstanceInternal() | |
151 removals.insert(DicomTag(0x0008, 0x0050)); // Accession Number | |
152 removals.insert(DicomTag(0x0008, 0x0080)); // Institution Name | |
153 removals.insert(DicomTag(0x0008, 0x0081)); // Institution Address | |
154 removals.insert(DicomTag(0x0008, 0x0090)); // Referring Physician's Name | |
155 removals.insert(DicomTag(0x0008, 0x0092)); // Referring Physician's Address | |
156 removals.insert(DicomTag(0x0008, 0x0094)); // Referring Physician's Telephone Numbers | |
157 removals.insert(DicomTag(0x0008, 0x1010)); // Station Name | |
158 removals.insert(DicomTag(0x0008, 0x1030)); // Study Description | |
159 removals.insert(DicomTag(0x0008, 0x103e)); // Series Description | |
160 removals.insert(DicomTag(0x0008, 0x1040)); // Institutional Department Name | |
161 removals.insert(DicomTag(0x0008, 0x1048)); // Physician(s) of Record | |
162 removals.insert(DicomTag(0x0008, 0x1050)); // Performing Physicians' Name | |
163 removals.insert(DicomTag(0x0008, 0x1060)); // Name of Physician(s) Reading Study | |
164 removals.insert(DicomTag(0x0008, 0x1070)); // Operators' Name | |
165 removals.insert(DicomTag(0x0008, 0x1080)); // Admitting Diagnoses Description | |
166 removals.insert(DicomTag(0x0008, 0x1155)); // Referenced SOP Instance UID | |
167 removals.insert(DicomTag(0x0008, 0x2111)); // Derivation Description | |
168 removals.insert(DicomTag(0x0010, 0x0010)); // Patient's Name | |
169 //removals.insert(DicomTag(0x0010, 0x0020)); // Patient ID => cf. below (*) | |
170 removals.insert(DicomTag(0x0010, 0x0030)); // Patient's Birth Date | |
171 removals.insert(DicomTag(0x0010, 0x0032)); // Patient's Birth Time | |
172 removals.insert(DicomTag(0x0010, 0x0040)); // Patient's Sex | |
173 removals.insert(DicomTag(0x0010, 0x1000)); // Other Patient Ids | |
174 removals.insert(DicomTag(0x0010, 0x1001)); // Other Patient Names | |
175 removals.insert(DicomTag(0x0010, 0x1010)); // Patient's Age | |
176 removals.insert(DicomTag(0x0010, 0x1020)); // Patient's Size | |
177 removals.insert(DicomTag(0x0010, 0x1030)); // Patient's Weight | |
178 removals.insert(DicomTag(0x0010, 0x1090)); // Medical Record Locator | |
179 removals.insert(DicomTag(0x0010, 0x2160)); // Ethnic Group | |
180 removals.insert(DicomTag(0x0010, 0x2180)); // Occupation | |
181 removals.insert(DicomTag(0x0010, 0x21b0)); // Additional Patient's History | |
182 removals.insert(DicomTag(0x0010, 0x4000)); // Patient Comments | |
183 removals.insert(DicomTag(0x0018, 0x1000)); // Device Serial Number | |
184 removals.insert(DicomTag(0x0018, 0x1030)); // Protocol Name | |
185 //removals.insert(DicomTag(0x0020, 0x000d)); // Study Instance UID => cf. below (*) | |
186 //removals.insert(DicomTag(0x0020, 0x000e)); // Series Instance UID => cf. below (*) | |
187 removals.insert(DicomTag(0x0020, 0x0010)); // Study ID | |
188 removals.insert(DicomTag(0x0020, 0x0052)); // Frame of Reference UID | |
189 removals.insert(DicomTag(0x0020, 0x0200)); // Synchronization Frame of Reference UID | |
190 removals.insert(DicomTag(0x0020, 0x4000)); // Image Comments | |
191 removals.insert(DicomTag(0x0040, 0x0275)); // Request Attributes Sequence | |
192 removals.insert(DicomTag(0x0040, 0xa124)); // UID | |
193 removals.insert(DicomTag(0x0040, 0xa730)); // Content Sequence | |
194 removals.insert(DicomTag(0x0088, 0x0140)); // Storage Media File-set UID | |
195 removals.insert(DicomTag(0x3006, 0x0024)); // Referenced Frame of Reference UID | |
196 removals.insert(DicomTag(0x3006, 0x00c2)); // Related Frame of Reference UID | |
197 | |
198 /** | |
199 * (*) Patient ID, Study Instance UID and Series Instance UID | |
200 * are modified by "AnonymizeInstance()" if anonymizing a single | |
201 * instance, or by "RetrieveMappedUid()" if anonymizing a | |
202 * patient/study/series. | |
203 **/ | |
204 | |
205 | |
206 // Some more removals (from the experience of DICOM files at the CHU of Liege) | |
207 removals.insert(DicomTag(0x0010, 0x1040)); // Patient's Address | |
208 removals.insert(DicomTag(0x0032, 0x1032)); // Requesting Physician | |
209 removals.insert(DicomTag(0x0010, 0x2154)); // PatientTelephoneNumbers | |
210 removals.insert(DicomTag(0x0010, 0x2000)); // Medical Alerts | |
211 | |
212 // Set the DeidentificationMethod tag | |
213 replacements.insert(std::make_pair(DicomTag(0x0012, 0x0063), "Orthanc " ORTHANC_VERSION " - PS 3.15-2008 Table E.1-1")); | |
214 | |
215 // Set the PatientIdentityRemoved tag | |
216 replacements.insert(std::make_pair(DicomTag(0x0012, 0x0062), "YES")); | |
217 } | |
218 | |
219 | |
220 static bool ParseModifyRequest(Removals& removals, | |
221 Replacements& replacements, | |
222 bool& removePrivateTags, | |
223 const RestApi::PostCall& call) | |
224 { | |
225 removePrivateTags = false; | |
226 Json::Value request; | |
227 if (call.ParseJsonRequest(request) && | |
228 request.isObject()) | |
229 { | |
230 Json::Value removalsPart = Json::arrayValue; | |
231 Json::Value replacementsPart = Json::objectValue; | |
232 | |
233 if (request.isMember("Remove")) | |
234 { | |
235 removalsPart = request["Remove"]; | |
236 } | |
237 | |
238 if (request.isMember("Replace")) | |
239 { | |
240 replacementsPart = request["Replace"]; | |
241 } | |
242 | |
243 if (request.isMember("RemovePrivateTags")) | |
244 { | |
245 removePrivateTags = true; | |
246 } | |
247 | |
248 ParseRemovals(removals, removalsPart); | |
249 ParseReplacements(replacements, replacementsPart); | |
250 | |
251 return true; | |
252 } | |
253 else | |
254 { | |
255 return false; | |
256 } | |
257 } | |
258 | |
259 | |
260 static bool ParseAnonymizationRequest(Removals& removals, | |
261 Replacements& replacements, | |
262 bool& removePrivateTags, | |
263 bool& keepPatientId, | |
264 RestApi::PostCall& call) | |
265 { | |
266 ServerContext& context = OrthancRestApi::GetContext(call); | |
267 | |
268 removePrivateTags = true; | |
269 keepPatientId = false; | |
270 | |
271 Json::Value request; | |
272 if (call.ParseJsonRequest(request) && | |
273 request.isObject()) | |
274 { | |
275 Json::Value keepPart = Json::arrayValue; | |
276 Json::Value removalsPart = Json::arrayValue; | |
277 Json::Value replacementsPart = Json::objectValue; | |
278 | |
279 if (request.isMember("Keep")) | |
280 { | |
281 keepPart = request["Keep"]; | |
282 } | |
283 | |
284 if (request.isMember("KeepPrivateTags")) | |
285 { | |
286 removePrivateTags = false; | |
287 } | |
288 | |
289 if (request.isMember("Replace")) | |
290 { | |
291 replacementsPart = request["Replace"]; | |
292 } | |
293 | |
294 Removals toKeep; | |
295 ParseRemovals(toKeep, keepPart); | |
296 | |
297 SetupAnonymization(removals, replacements); | |
298 | |
299 for (Removals::iterator it = toKeep.begin(); it != toKeep.end(); ++it) | |
300 { | |
301 if (*it == DICOM_TAG_PATIENT_ID) | |
302 { | |
303 keepPatientId = true; | |
304 } | |
305 | |
306 removals.erase(*it); | |
307 } | |
308 | |
309 Removals additionalRemovals; | |
310 ParseRemovals(additionalRemovals, removalsPart); | |
311 | |
312 for (Removals::iterator it = additionalRemovals.begin(); | |
313 it != additionalRemovals.end(); ++it) | |
314 { | |
315 removals.insert(*it); | |
316 } | |
317 | |
318 ParseReplacements(replacements, replacementsPart); | |
319 | |
320 // Generate random Patient's Name if none is specified | |
321 if (toKeep.find(DICOM_TAG_PATIENT_NAME) == toKeep.end() && | |
322 replacements.find(DICOM_TAG_PATIENT_NAME) == replacements.end()) | |
323 { | |
324 replacements.insert(std::make_pair(DICOM_TAG_PATIENT_NAME, GeneratePatientName(context))); | |
325 } | |
326 | |
327 return true; | |
328 } | |
329 else | |
330 { | |
331 return false; | |
332 } | |
333 } | |
334 | |
335 | |
336 static void AnonymizeOrModifyInstance(Removals& removals, | |
337 Replacements& replacements, | |
338 bool removePrivateTags, | |
339 RestApi::PostCall& call) | |
340 { | |
341 boost::mutex::scoped_lock lock(cacheMutex_); | |
342 ServerContext& context = OrthancRestApi::GetContext(call); | |
343 | |
344 std::string id = call.GetUriComponent("id", ""); | |
345 ParsedDicomFile& dicom = context.GetDicomFile(id); | |
346 | |
347 std::auto_ptr<ParsedDicomFile> modified(dicom.Clone()); | |
348 ReplaceInstanceInternal(*modified, removals, replacements, DicomReplaceMode_InsertIfAbsent, removePrivateTags); | |
349 modified->Answer(call.GetOutput()); | |
350 } | |
351 | |
352 | |
353 static bool RetrieveMappedUid(ParsedDicomFile& dicom, | |
354 DicomRootLevel level, | |
355 Replacements& replacements, | |
356 UidMap& uidMap) | |
357 { | |
358 std::auto_ptr<DicomTag> tag; | |
359 | |
360 switch (level) | |
361 { | |
362 case DicomRootLevel_Series: | |
363 tag.reset(new DicomTag(DICOM_TAG_SERIES_INSTANCE_UID)); | |
364 break; | |
365 | |
366 case DicomRootLevel_Study: | |
367 tag.reset(new DicomTag(DICOM_TAG_STUDY_INSTANCE_UID)); | |
368 break; | |
369 | |
370 case DicomRootLevel_Patient: | |
371 tag.reset(new DicomTag(DICOM_TAG_PATIENT_ID)); | |
372 break; | |
373 | |
374 default: | |
375 throw OrthancException(ErrorCode_InternalError); | |
376 } | |
377 | |
378 std::string original; | |
379 if (!dicom.GetTagValue(original, *tag)) | |
380 { | |
381 throw OrthancException(ErrorCode_InternalError); | |
382 } | |
383 | |
384 std::string mapped; | |
385 bool isNew; | |
386 | |
387 UidMap::const_iterator previous = uidMap.find(std::make_pair(level, original)); | |
388 if (previous == uidMap.end()) | |
389 { | |
390 mapped = FromDcmtkBridge::GenerateUniqueIdentifier(level); | |
391 uidMap.insert(std::make_pair(std::make_pair(level, original), mapped)); | |
392 isNew = true; | |
393 } | |
394 else | |
395 { | |
396 mapped = previous->second; | |
397 isNew = false; | |
398 } | |
399 | |
400 replacements[*tag] = mapped; | |
401 return isNew; | |
402 } | |
403 | |
404 | |
405 static void AnonymizeOrModifyResource(Removals& removals, | |
406 Replacements& replacements, | |
407 bool removePrivateTags, | |
408 bool keepPatientId, | |
409 MetadataType metadataType, | |
410 ChangeType changeType, | |
411 ResourceType resourceType, | |
412 RestApi::PostCall& call) | |
413 { | |
414 typedef std::list<std::string> Instances; | |
415 | |
416 bool isFirst = true; | |
417 Json::Value result(Json::objectValue); | |
418 | |
419 boost::mutex::scoped_lock lock(cacheMutex_); | |
420 ServerContext& context = OrthancRestApi::GetContext(call); | |
421 | |
422 Instances instances; | |
423 std::string id = call.GetUriComponent("id", ""); | |
424 context.GetIndex().GetChildInstances(instances, id); | |
425 | |
426 if (instances.empty()) | |
427 { | |
428 return; | |
429 } | |
430 | |
431 /** | |
432 * Loop over all the instances of the resource. | |
433 **/ | |
434 | |
435 UidMap uidMap; | |
436 for (Instances::const_iterator it = instances.begin(); | |
437 it != instances.end(); ++it) | |
438 { | |
439 LOG(INFO) << "Modifying instance " << *it; | |
440 ParsedDicomFile& original = context.GetDicomFile(*it); | |
441 | |
442 DicomInstanceHasher originalHasher = original.GetHasher(); | |
443 | |
444 if (isFirst && keepPatientId) | |
445 { | |
446 std::string patientId = originalHasher.GetPatientId(); | |
447 uidMap[std::make_pair(DicomRootLevel_Patient, patientId)] = patientId; | |
448 } | |
449 | |
450 bool isNewSeries = RetrieveMappedUid(original, DicomRootLevel_Series, replacements, uidMap); | |
451 bool isNewStudy = RetrieveMappedUid(original, DicomRootLevel_Study, replacements, uidMap); | |
452 bool isNewPatient = RetrieveMappedUid(original, DicomRootLevel_Patient, replacements, uidMap); | |
453 | |
454 | |
455 /** | |
456 * Compute the resulting DICOM instance and store it into the Orthanc store. | |
457 **/ | |
458 | |
459 std::auto_ptr<ParsedDicomFile> modified(original.Clone()); | |
460 ReplaceInstanceInternal(*modified, removals, replacements, DicomReplaceMode_InsertIfAbsent, removePrivateTags); | |
461 | |
462 std::string modifiedInstance; | |
463 if (context.Store(modifiedInstance, modified->GetDicom()) != StoreStatus_Success) | |
464 { | |
465 LOG(ERROR) << "Error while storing a modified instance " << *it; | |
466 return; | |
467 } | |
468 | |
469 | |
470 /** | |
471 * Record metadata information (AnonymizedFrom/ModifiedFrom). | |
472 **/ | |
473 | |
474 DicomInstanceHasher modifiedHasher = modified->GetHasher(); | |
475 | |
476 if (isNewSeries) | |
477 { | |
478 context.GetIndex().SetMetadata(modifiedHasher.HashSeries(), | |
479 metadataType, originalHasher.HashSeries()); | |
480 } | |
481 | |
482 if (isNewStudy) | |
483 { | |
484 context.GetIndex().SetMetadata(modifiedHasher.HashStudy(), | |
485 metadataType, originalHasher.HashStudy()); | |
486 } | |
487 | |
488 if (isNewPatient) | |
489 { | |
490 context.GetIndex().SetMetadata(modifiedHasher.HashPatient(), | |
491 metadataType, originalHasher.HashPatient()); | |
492 } | |
493 | |
494 assert(*it == originalHasher.HashInstance()); | |
495 assert(modifiedInstance == modifiedHasher.HashInstance()); | |
496 context.GetIndex().SetMetadata(modifiedInstance, metadataType, *it); | |
497 | |
498 | |
499 /** | |
500 * Compute the JSON object that is returned by the REST call. | |
501 **/ | |
502 | |
503 if (isFirst) | |
504 { | |
505 std::string newId; | |
506 | |
507 switch (resourceType) | |
508 { | |
509 case ResourceType_Series: | |
510 newId = modifiedHasher.HashSeries(); | |
511 break; | |
512 | |
513 case ResourceType_Study: | |
514 newId = modifiedHasher.HashStudy(); | |
515 break; | |
516 | |
517 case ResourceType_Patient: | |
518 newId = modifiedHasher.HashPatient(); | |
519 break; | |
520 | |
521 default: | |
522 throw OrthancException(ErrorCode_InternalError); | |
523 } | |
524 | |
525 result["Type"] = EnumerationToString(resourceType); | |
526 result["ID"] = newId; | |
527 result["Path"] = GetBasePath(resourceType, newId); | |
528 result["PatientID"] = modifiedHasher.HashPatient(); | |
529 isFirst = false; | |
530 } | |
531 } | |
532 | |
533 call.GetOutput().AnswerJson(result); | |
534 } | |
535 | |
536 | |
537 | |
538 static void ModifyInstance(RestApi::PostCall& call) | |
539 { | |
540 Removals removals; | |
541 Replacements replacements; | |
542 bool removePrivateTags; | |
543 | |
544 if (ParseModifyRequest(removals, replacements, removePrivateTags, call)) | |
545 { | |
546 AnonymizeOrModifyInstance(removals, replacements, removePrivateTags, call); | |
547 } | |
548 } | |
549 | |
550 | |
551 static void AnonymizeInstance(RestApi::PostCall& call) | |
552 { | |
553 Removals removals; | |
554 Replacements replacements; | |
555 bool removePrivateTags, keepPatientId; | |
556 | |
557 if (ParseAnonymizationRequest(removals, replacements, removePrivateTags, keepPatientId, call)) | |
558 { | |
559 // TODO Handle "keepPatientId" | |
560 | |
561 // Generate random patient ID if not specified | |
562 if (replacements.find(DICOM_TAG_PATIENT_ID) == replacements.end()) | |
563 { | |
564 replacements.insert(std::make_pair(DICOM_TAG_PATIENT_ID, | |
565 FromDcmtkBridge::GenerateUniqueIdentifier(DicomRootLevel_Patient))); | |
566 } | |
567 | |
568 // Generate random study UID if not specified | |
569 if (replacements.find(DICOM_TAG_STUDY_INSTANCE_UID) == replacements.end()) | |
570 { | |
571 replacements.insert(std::make_pair(DICOM_TAG_STUDY_INSTANCE_UID, | |
572 FromDcmtkBridge::GenerateUniqueIdentifier(DicomRootLevel_Study))); | |
573 } | |
574 | |
575 // Generate random series UID if not specified | |
576 if (replacements.find(DICOM_TAG_SERIES_INSTANCE_UID) == replacements.end()) | |
577 { | |
578 replacements.insert(std::make_pair(DICOM_TAG_SERIES_INSTANCE_UID, | |
579 FromDcmtkBridge::GenerateUniqueIdentifier(DicomRootLevel_Series))); | |
580 } | |
581 | |
582 AnonymizeOrModifyInstance(removals, replacements, removePrivateTags, call); | |
583 } | |
584 } | |
585 | |
586 | |
587 static void ModifySeriesInplace(RestApi::PostCall& call) | |
588 { | |
589 Removals removals; | |
590 Replacements replacements; | |
591 bool removePrivateTags; | |
592 | |
593 if (ParseModifyRequest(removals, replacements, removePrivateTags, call)) | |
594 { | |
595 AnonymizeOrModifyResource(removals, replacements, removePrivateTags, true /*keepPatientId*/, | |
596 MetadataType_ModifiedFrom, ChangeType_ModifiedSeries, | |
597 ResourceType_Series, call); | |
598 } | |
599 } | |
600 | |
601 | |
602 static void AnonymizeSeriesInplace(RestApi::PostCall& call) | |
603 { | |
604 Removals removals; | |
605 Replacements replacements; | |
606 bool removePrivateTags, keepPatientId; | |
607 | |
608 if (ParseAnonymizationRequest(removals, replacements, removePrivateTags, keepPatientId, call)) | |
609 { | |
610 AnonymizeOrModifyResource(removals, replacements, removePrivateTags, keepPatientId, | |
611 MetadataType_AnonymizedFrom, ChangeType_AnonymizedSeries, | |
612 ResourceType_Series, call); | |
613 } | |
614 } | |
615 | |
616 | |
617 static void ModifyStudyInplace(RestApi::PostCall& call) | |
618 { | |
619 Removals removals; | |
620 Replacements replacements; | |
621 bool removePrivateTags; | |
622 | |
623 if (ParseModifyRequest(removals, replacements, removePrivateTags, call)) | |
624 { | |
625 AnonymizeOrModifyResource(removals, replacements, removePrivateTags, true /*keepPatientId*/, | |
626 MetadataType_ModifiedFrom, ChangeType_ModifiedStudy, | |
627 ResourceType_Study, call); | |
628 } | |
629 } | |
630 | |
631 | |
632 static void AnonymizeStudyInplace(RestApi::PostCall& call) | |
633 { | |
634 Removals removals; | |
635 Replacements replacements; | |
636 bool removePrivateTags, keepPatientId; | |
637 | |
638 if (ParseAnonymizationRequest(removals, replacements, removePrivateTags, keepPatientId, call)) | |
639 { | |
640 AnonymizeOrModifyResource(removals, replacements, removePrivateTags, keepPatientId, | |
641 MetadataType_AnonymizedFrom, ChangeType_AnonymizedStudy, | |
642 ResourceType_Study, call); | |
643 } | |
644 } | |
645 | |
646 | |
647 /*static void ModifyPatientInplace(RestApi::PostCall& call) | |
648 { | |
649 Removals removals; | |
650 Replacements replacements; | |
651 bool removePrivateTags; | |
652 | |
653 if (ParseModifyRequest(removals, replacements, removePrivateTags, call)) | |
654 { | |
655 AnonymizeOrModifyResource(false, removals, replacements, removePrivateTags, | |
656 MetadataType_ModifiedFrom, ChangeType_ModifiedPatient, | |
657 ResourceType_Patient, call); | |
658 } | |
659 }*/ | |
660 | |
661 | |
662 static void AnonymizePatientInplace(RestApi::PostCall& call) | |
663 { | |
664 Removals removals; | |
665 Replacements replacements; | |
666 bool removePrivateTags, keepPatientId; | |
667 | |
668 if (ParseAnonymizationRequest(removals, replacements, removePrivateTags, keepPatientId, call)) | |
669 { | |
670 AnonymizeOrModifyResource(removals, replacements, removePrivateTags, keepPatientId, | |
671 MetadataType_AnonymizedFrom, ChangeType_AnonymizedPatient, | |
672 ResourceType_Patient, call); | |
673 } | |
674 } | |
675 | |
676 | |
677 | |
678 void OrthancRestApi::RegisterAnonymizeModify() | |
679 { | |
680 Register("/instances/{id}/content/*", GetRawContent); | |
681 | |
682 Register("/instances/{id}/modify", ModifyInstance); | |
683 Register("/series/{id}/modify", ModifySeriesInplace); | |
684 Register("/studies/{id}/modify", ModifyStudyInplace); | |
685 //Register("/patients/{id}/modify", ModifyPatientInplace); | |
686 | |
687 Register("/instances/{id}/anonymize", AnonymizeInstance); | |
688 Register("/series/{id}/anonymize", AnonymizeSeriesInplace); | |
689 Register("/studies/{id}/anonymize", AnonymizeStudyInplace); | |
690 Register("/patients/{id}/anonymize", AnonymizePatientInplace); | |
691 } | |
692 } |