Mercurial > hg > orthanc
comparison OrthancServer/OrthancRestApi/OrthancRestAnonymizeModify.cpp @ 2519:2e6b7862ccf2
ParseAnonymizationRequest/ParseModifyRequest now in DicomModification
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Thu, 29 Mar 2018 14:52:11 +0200 |
parents | 63d2cc0fb40a |
children | 6db878376018 |
comparison
equal
deleted
inserted
replaced
2518:63d2cc0fb40a | 2519:2e6b7862ccf2 |
---|---|
44 | 44 |
45 namespace Orthanc | 45 namespace Orthanc |
46 { | 46 { |
47 // Modification of DICOM instances ------------------------------------------ | 47 // Modification of DICOM instances ------------------------------------------ |
48 | 48 |
49 enum TagOperation | 49 |
50 { | |
51 TagOperation_Keep, | |
52 TagOperation_Remove | |
53 }; | |
54 | |
55 static bool IsDatabaseKey(const DicomTag& tag) | |
56 { | |
57 return (tag == DICOM_TAG_PATIENT_ID || | |
58 tag == DICOM_TAG_STUDY_INSTANCE_UID || | |
59 tag == DICOM_TAG_SERIES_INSTANCE_UID || | |
60 tag == DICOM_TAG_SOP_INSTANCE_UID); | |
61 } | |
62 | |
63 static void ParseListOfTags(DicomModification& target, | |
64 const Json::Value& query, | |
65 TagOperation operation, | |
66 bool force) | |
67 { | |
68 if (!query.isArray()) | |
69 { | |
70 throw OrthancException(ErrorCode_BadRequest); | |
71 } | |
72 | |
73 for (Json::Value::ArrayIndex i = 0; i < query.size(); i++) | |
74 { | |
75 std::string name = query[i].asString(); | |
76 | |
77 DicomTag tag = FromDcmtkBridge::ParseTag(name); | |
78 | |
79 if (!force && IsDatabaseKey(tag)) | |
80 { | |
81 LOG(ERROR) << "Marking tag \"" << name << "\" as to be " | |
82 << (operation == TagOperation_Keep ? "kept" : "removed") | |
83 << " requires the \"Force\" option to be set to true"; | |
84 throw OrthancException(ErrorCode_BadRequest); | |
85 } | |
86 | |
87 switch (operation) | |
88 { | |
89 case TagOperation_Keep: | |
90 target.Keep(tag); | |
91 VLOG(1) << "Keep: " << name << " " << tag << std::endl; | |
92 break; | |
93 | |
94 case TagOperation_Remove: | |
95 target.Remove(tag); | |
96 VLOG(1) << "Remove: " << name << " " << tag << std::endl; | |
97 break; | |
98 | |
99 default: | |
100 throw OrthancException(ErrorCode_InternalError); | |
101 } | |
102 } | |
103 } | |
104 | |
105 | |
106 static void ParseReplacements(DicomModification& target, | |
107 const Json::Value& replacements, | |
108 bool force) | |
109 { | |
110 if (!replacements.isObject()) | |
111 { | |
112 throw OrthancException(ErrorCode_BadRequest); | |
113 } | |
114 | |
115 Json::Value::Members members = replacements.getMemberNames(); | |
116 for (size_t i = 0; i < members.size(); i++) | |
117 { | |
118 const std::string& name = members[i]; | |
119 const Json::Value& value = replacements[name]; | |
120 | |
121 DicomTag tag = FromDcmtkBridge::ParseTag(name); | |
122 | |
123 if (!force && IsDatabaseKey(tag)) | |
124 { | |
125 LOG(ERROR) << "Marking tag \"" << name << "\" as to be replaced " | |
126 << "requires the \"Force\" option to be set to true"; | |
127 throw OrthancException(ErrorCode_BadRequest); | |
128 } | |
129 | |
130 target.Replace(tag, value, false); | |
131 | |
132 VLOG(1) << "Replace: " << name << " " << tag | |
133 << " == " << value.toStyledString() << std::endl; | |
134 } | |
135 } | |
136 | |
137 | |
138 static std::string GeneratePatientName(ServerContext& context) | 50 static std::string GeneratePatientName(ServerContext& context) |
139 { | 51 { |
140 uint64_t seq = context.GetIndex().IncrementGlobalSequence(GlobalProperty_AnonymizationSequence); | 52 uint64_t seq = context.GetIndex().IncrementGlobalSequence(GlobalProperty_AnonymizationSequence); |
141 return "Anonymized" + boost::lexical_cast<std::string>(seq); | 53 return "Anonymized" + boost::lexical_cast<std::string>(seq); |
142 } | 54 } |
143 | 55 |
144 | 56 |
145 static bool GetBooleanValue(const std::string& member, | |
146 const Json::Value& json, | |
147 bool defaultValue) | |
148 { | |
149 if (!json.isMember(member)) | |
150 { | |
151 return defaultValue; | |
152 } | |
153 else if (json[member].type() == Json::booleanValue) | |
154 { | |
155 return json[member].asBool(); | |
156 } | |
157 else | |
158 { | |
159 LOG(ERROR) << "Member \"" << member << "\" should be a Boolean value"; | |
160 throw OrthancException(ErrorCode_BadFileFormat); | |
161 } | |
162 } | |
163 | |
164 | |
165 void OrthancRestApi::ParseModifyRequest(DicomModification& target, | |
166 const Json::Value& request) | |
167 { | |
168 if (!request.isObject()) | |
169 { | |
170 throw OrthancException(ErrorCode_BadFileFormat); | |
171 } | |
172 | |
173 bool force = GetBooleanValue("Force", request, false); | |
174 | |
175 if (GetBooleanValue("RemovePrivateTags", request, false)) | |
176 { | |
177 target.SetRemovePrivateTags(true); | |
178 } | |
179 | |
180 if (request.isMember("Remove")) | |
181 { | |
182 ParseListOfTags(target, request["Remove"], TagOperation_Remove, force); | |
183 } | |
184 | |
185 if (request.isMember("Replace")) | |
186 { | |
187 ParseReplacements(target, request["Replace"], force); | |
188 } | |
189 | |
190 // The "Keep" operation only makes sense for the tags | |
191 // StudyInstanceUID, SeriesInstanceUID and SOPInstanceUID. Avoid | |
192 // this feature as much as possible, as this breaks the DICOM | |
193 // model of the real world, except if you know exactly what | |
194 // you're doing! | |
195 if (request.isMember("Keep")) | |
196 { | |
197 ParseListOfTags(target, request["Keep"], TagOperation_Keep, force); | |
198 } | |
199 } | |
200 | |
201 | |
202 static void ParseAnonymizationRequest(DicomModification& target, | |
203 bool& patientNameReplaced, | |
204 const Json::Value& request) | |
205 { | |
206 if (!request.isObject()) | |
207 { | |
208 throw OrthancException(ErrorCode_BadFileFormat); | |
209 } | |
210 | |
211 bool force = GetBooleanValue("Force", request, false); | |
212 | |
213 // As of Orthanc 1.3.0, the default anonymization is done | |
214 // according to PS 3.15-2017c Table E.1-1 (basic profile) | |
215 DicomVersion version = DicomVersion_2017c; | |
216 if (request.isMember("DicomVersion")) | |
217 { | |
218 if (request["DicomVersion"].type() != Json::stringValue) | |
219 { | |
220 throw OrthancException(ErrorCode_BadFileFormat); | |
221 } | |
222 else | |
223 { | |
224 version = StringToDicomVersion(request["DicomVersion"].asString()); | |
225 } | |
226 } | |
227 | |
228 target.SetupAnonymization(version); | |
229 | |
230 std::string patientName = target.GetReplacementAsString(DICOM_TAG_PATIENT_NAME); | |
231 | |
232 if (GetBooleanValue("KeepPrivateTags", request, false)) | |
233 { | |
234 target.SetRemovePrivateTags(false); | |
235 } | |
236 | |
237 if (request.isMember("Remove")) | |
238 { | |
239 ParseListOfTags(target, request["Remove"], TagOperation_Remove, force); | |
240 } | |
241 | |
242 if (request.isMember("Replace")) | |
243 { | |
244 ParseReplacements(target, request["Replace"], force); | |
245 } | |
246 | |
247 if (request.isMember("Keep")) | |
248 { | |
249 ParseListOfTags(target, request["Keep"], TagOperation_Keep, force); | |
250 } | |
251 | |
252 patientNameReplaced = (target.IsReplaced(DICOM_TAG_PATIENT_NAME) && | |
253 target.GetReplacement(DICOM_TAG_PATIENT_NAME) == patientName); | |
254 } | |
255 | |
256 | |
257 static void ParseModifyRequest(DicomModification& target, | 57 static void ParseModifyRequest(DicomModification& target, |
258 const RestApiPostCall& call) | 58 const RestApiPostCall& call) |
259 { | 59 { |
260 // curl http://localhost:8042/series/95a6e2bf-9296e2cc-bf614e2f-22b391ee-16e010e0/modify -X POST -d '{"Replace":{"InstitutionName":"My own clinic"}}' | 60 // curl http://localhost:8042/series/95a6e2bf-9296e2cc-bf614e2f-22b391ee-16e010e0/modify -X POST -d '{"Replace":{"InstitutionName":"My own clinic"}}' |
261 | 61 |
262 Json::Value request; | 62 Json::Value request; |
263 if (call.ParseJsonRequest(request)) | 63 if (call.ParseJsonRequest(request)) |
264 { | 64 { |
265 OrthancRestApi::ParseModifyRequest(target, request); | 65 target.ParseModifyRequest(request); |
266 } | 66 } |
267 else | 67 else |
268 { | 68 { |
269 throw OrthancException(ErrorCode_BadFileFormat); | 69 throw OrthancException(ErrorCode_BadFileFormat); |
270 } | 70 } |
279 Json::Value request; | 79 Json::Value request; |
280 if (call.ParseJsonRequest(request) && | 80 if (call.ParseJsonRequest(request) && |
281 request.isObject()) | 81 request.isObject()) |
282 { | 82 { |
283 bool patientNameReplaced; | 83 bool patientNameReplaced; |
284 ParseAnonymizationRequest(target, patientNameReplaced, request); | 84 target.ParseAnonymizationRequest(patientNameReplaced, request); |
285 | 85 |
286 if (patientNameReplaced) | 86 if (patientNameReplaced) |
287 { | 87 { |
288 // Overwrite the random Patient's Name by one that is more | 88 // Overwrite the random Patient's Name by one that is more |
289 // user-friendly (provided none was specified by the user) | 89 // user-friendly (provided none was specified by the user) |