Mercurial > hg > orthanc
annotate OrthancServer/Plugins/Samples/DelayedDeletion/LargeDeleteJob.cpp @ 5487:33f8e180edcf
upgraded static build to dcmtk 3.6.8
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Tue, 09 Jan 2024 17:34:33 +0100 |
parents | 48b8dae6dc77 |
children | f7adfb22e20e |
rev | line source |
---|---|
5264
99751c5a7cfe
added missing copyrights
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
5024
diff
changeset
|
1 /** |
99751c5a7cfe
added missing copyrights
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
5024
diff
changeset
|
2 * Orthanc - A Lightweight, RESTful DICOM Store |
99751c5a7cfe
added missing copyrights
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
5024
diff
changeset
|
3 * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics |
99751c5a7cfe
added missing copyrights
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
5024
diff
changeset
|
4 * Department, University Hospital of Liege, Belgium |
5485
48b8dae6dc77
upgrade to year 2024
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
5280
diff
changeset
|
5 * Copyright (C) 2017-2024 Osimis S.A., Belgium |
48b8dae6dc77
upgrade to year 2024
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
5280
diff
changeset
|
6 * Copyright (C) 2021-2024 Sebastien Jodogne, ICTEAM UCLouvain, Belgium |
5264
99751c5a7cfe
added missing copyrights
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
5024
diff
changeset
|
7 * |
99751c5a7cfe
added missing copyrights
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
5024
diff
changeset
|
8 * This program is free software: you can redistribute it and/or |
99751c5a7cfe
added missing copyrights
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
5024
diff
changeset
|
9 * modify it under the terms of the GNU General Public License as |
99751c5a7cfe
added missing copyrights
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
5024
diff
changeset
|
10 * published by the Free Software Foundation, either version 3 of the |
99751c5a7cfe
added missing copyrights
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
5024
diff
changeset
|
11 * License, or (at your option) any later version. |
99751c5a7cfe
added missing copyrights
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
5024
diff
changeset
|
12 * |
99751c5a7cfe
added missing copyrights
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
5024
diff
changeset
|
13 * This program is distributed in the hope that it will be useful, but |
99751c5a7cfe
added missing copyrights
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
5024
diff
changeset
|
14 * WITHOUT ANY WARRANTY; without even the implied warranty of |
99751c5a7cfe
added missing copyrights
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
5024
diff
changeset
|
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
99751c5a7cfe
added missing copyrights
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
5024
diff
changeset
|
16 * General Public License for more details. |
99751c5a7cfe
added missing copyrights
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
5024
diff
changeset
|
17 * |
99751c5a7cfe
added missing copyrights
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
5024
diff
changeset
|
18 * You should have received a copy of the GNU General Public License |
99751c5a7cfe
added missing copyrights
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
5024
diff
changeset
|
19 * along with this program. If not, see <http://www.gnu.org/licenses/>. |
99751c5a7cfe
added missing copyrights
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
5024
diff
changeset
|
20 **/ |
99751c5a7cfe
added missing copyrights
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
5024
diff
changeset
|
21 |
99751c5a7cfe
added missing copyrights
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
5024
diff
changeset
|
22 |
5024 | 23 #include "LargeDeleteJob.h" |
24 | |
25 #include "../../../../OrthancFramework/Sources/Logging.h" | |
26 #include "../../../../OrthancFramework/Sources/OrthancException.h" | |
27 | |
28 #include <json/reader.h> | |
29 | |
30 void LargeDeleteJob::UpdateDeleteProgress() | |
31 { | |
32 size_t total = 2 * resources_.size() + instances_.size() + series_.size(); | |
33 | |
34 float progress; | |
35 if (total == 0) | |
36 { | |
37 progress = 1; | |
38 } | |
39 else | |
40 { | |
41 progress = (static_cast<float>(posResources_ + posInstances_ + posSeries_ + posDelete_) / | |
42 static_cast<float>(total)); | |
43 } | |
44 | |
45 UpdateProgress(progress); | |
46 } | |
47 | |
48 | |
49 void LargeDeleteJob::ScheduleChildrenResources(std::vector<std::string>& target, | |
50 const std::string& uri) | |
51 { | |
52 Json::Value items; | |
53 if (OrthancPlugins::RestApiGet(items, uri, false)) | |
54 { | |
55 if (items.type() != Json::arrayValue) | |
56 { | |
57 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); | |
58 } | |
59 | |
60 for (Json::Value::ArrayIndex i = 0; i < items.size(); i++) | |
61 { | |
62 if (items[i].type() != Json::objectValue || | |
63 !items[i].isMember("ID") || | |
64 items[i]["ID"].type() != Json::stringValue) | |
65 { | |
66 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); | |
67 } | |
68 else | |
69 { | |
70 target.push_back(items[i]["ID"].asString()); | |
71 } | |
72 } | |
73 } | |
74 } | |
75 | |
76 | |
77 void LargeDeleteJob::ScheduleResource(Orthanc::ResourceType level, | |
78 const std::string& id) | |
79 { | |
80 #if 0 | |
81 // Instance-level granularity => very slow! | |
82 switch (level) | |
83 { | |
84 case Orthanc::ResourceType_Patient: | |
85 ScheduleChildrenResources(instances_, "/patients/" + id + "/instances"); | |
86 break; | |
87 | |
88 case Orthanc::ResourceType_Study: | |
89 ScheduleChildrenResources(instances_, "/studies/" + id + "/instances"); | |
90 break; | |
91 | |
92 case Orthanc::ResourceType_Series: | |
93 ScheduleChildrenResources(instances_, "/series/" + id + "/instances"); | |
94 break; | |
95 | |
96 case Orthanc::ResourceType_Instance: | |
97 instances_.push_back(id); | |
98 break; | |
99 | |
100 default: | |
101 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); | |
102 } | |
103 #else | |
104 /** | |
105 * Series-level granularity => looks like a good compromise between | |
106 * having the Orthanc mutex locked during all the study, and very | |
107 * slow instance-level granularity. | |
108 **/ | |
109 switch (level) | |
110 { | |
111 case Orthanc::ResourceType_Patient: | |
112 ScheduleChildrenResources(series_, "/patients/" + id + "/series"); | |
113 break; | |
114 | |
115 case Orthanc::ResourceType_Study: | |
116 ScheduleChildrenResources(series_, "/studies/" + id + "/series"); | |
117 break; | |
118 | |
119 case Orthanc::ResourceType_Series: | |
120 series_.push_back(id); | |
121 break; | |
122 | |
123 case Orthanc::ResourceType_Instance: | |
124 instances_.push_back(id); | |
125 break; | |
126 | |
127 default: | |
128 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); | |
129 } | |
130 #endif | |
131 } | |
132 | |
133 | |
134 void LargeDeleteJob::DeleteResource(Orthanc::ResourceType level, | |
135 const std::string& id) | |
136 { | |
137 std::string uri; | |
138 switch (level) | |
139 { | |
140 case Orthanc::ResourceType_Patient: | |
141 uri = "/patients/" + id; | |
142 break; | |
143 | |
144 case Orthanc::ResourceType_Study: | |
145 uri = "/studies/" + id; | |
146 break; | |
147 | |
148 case Orthanc::ResourceType_Series: | |
149 uri = "/series/" + id; | |
150 break; | |
151 | |
152 case Orthanc::ResourceType_Instance: | |
153 uri = "/instances/" + id; | |
154 break; | |
155 | |
156 default: | |
157 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); | |
158 } | |
159 | |
160 OrthancPlugins::RestApiDelete(uri, false); | |
161 } | |
162 | |
163 | |
164 LargeDeleteJob::LargeDeleteJob(const std::vector<std::string>& resources, | |
165 const std::vector<Orthanc::ResourceType>& levels) : | |
166 OrthancJob("LargeDelete"), | |
167 resources_(resources), | |
168 levels_(levels), | |
169 posResources_(0), | |
170 posInstances_(0), | |
5280 | 171 posSeries_(0), |
5024 | 172 posDelete_(0) |
173 { | |
174 if (resources.size() != levels.size()) | |
175 { | |
176 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); | |
177 } | |
178 } | |
179 | |
180 | |
181 OrthancPluginJobStepStatus LargeDeleteJob::Step() | |
182 { | |
183 if (posResources_ == 0) | |
184 { | |
185 if (resources_.size() == 1) | |
186 { | |
187 // LOG(WARNING) << "LargeDeleteJob has started on resource: " << resources_[0]; | |
188 } | |
189 else | |
190 { | |
191 // LOG(WARNING) << "LargeDeleteJob has started"; | |
192 } | |
193 } | |
194 | |
195 if (posResources_ < resources_.size()) | |
196 { | |
197 // First step: Discovering all the instances of the resources | |
198 | |
199 ScheduleResource(levels_[posResources_], resources_[posResources_]); | |
200 | |
201 posResources_ += 1; | |
202 UpdateDeleteProgress(); | |
203 return OrthancPluginJobStepStatus_Continue; | |
204 } | |
205 else if (posInstances_ < instances_.size()) | |
206 { | |
207 // Second step: Deleting the instances one by one | |
208 | |
209 DeleteResource(Orthanc::ResourceType_Instance, instances_[posInstances_]); | |
210 | |
211 posInstances_ += 1; | |
212 UpdateDeleteProgress(); | |
213 return OrthancPluginJobStepStatus_Continue; | |
214 } | |
215 else if (posSeries_ < series_.size()) | |
216 { | |
217 // Third step: Deleting the series one by one | |
218 | |
219 DeleteResource(Orthanc::ResourceType_Series, series_[posSeries_]); | |
220 | |
221 posSeries_ += 1; | |
222 UpdateDeleteProgress(); | |
223 return OrthancPluginJobStepStatus_Continue; | |
224 } | |
225 else if (posDelete_ < resources_.size()) | |
226 { | |
227 // Fourth step: Make sure the resources where fully deleted | |
228 // (instances might have been received since the beginning of | |
229 // the job) | |
230 | |
231 DeleteResource(levels_[posDelete_], resources_[posDelete_]); | |
232 | |
233 posDelete_ += 1; | |
234 UpdateDeleteProgress(); | |
235 return OrthancPluginJobStepStatus_Continue; | |
236 } | |
237 else | |
238 { | |
239 if (resources_.size() == 1) | |
240 { | |
241 // LOG(WARNING) << "LargeDeleteJob has completed on resource: " << resources_[0]; | |
242 } | |
243 else | |
244 { | |
245 // LOG(WARNING) << "LargeDeleteJob has completed"; | |
246 } | |
247 | |
248 UpdateProgress(1); | |
249 return OrthancPluginJobStepStatus_Success; | |
250 } | |
251 } | |
252 | |
253 | |
254 void LargeDeleteJob::Reset() | |
255 { | |
256 posResources_ = 0; | |
257 posInstances_ = 0; | |
258 posDelete_ = 0; | |
259 instances_.clear(); | |
260 } | |
261 | |
262 | |
263 void LargeDeleteJob::RestHandler(OrthancPluginRestOutput* output, | |
264 const char* url, | |
265 const OrthancPluginHttpRequest* request) | |
266 { | |
267 static const char* KEY_RESOURCES = "Resources"; | |
268 | |
269 if (request->method != OrthancPluginHttpMethod_Post) | |
270 { | |
271 OrthancPluginSendMethodNotAllowed(OrthancPlugins::GetGlobalContext(), output, "POST"); | |
272 return; | |
273 } | |
274 | |
275 Json::Value body; | |
276 Json::Reader reader; | |
277 if (!reader.parse(reinterpret_cast<const char*>(request->body), | |
278 reinterpret_cast<const char*>(request->body) + request->bodySize, body)) | |
279 { | |
280 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat, "JSON body is expected"); | |
281 } | |
282 | |
283 if (body.type() != Json::objectValue) | |
284 { | |
285 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat, | |
286 "Expected a JSON object in the body"); | |
287 } | |
288 | |
289 if (!body.isMember(KEY_RESOURCES) || | |
290 body[KEY_RESOURCES].type() != Json::arrayValue) | |
291 { | |
292 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat, | |
293 "The JSON object must contain an array in \"" + | |
294 std::string(KEY_RESOURCES) + "\""); | |
295 } | |
296 | |
297 std::vector<std::string> resources; | |
298 std::vector<Orthanc::ResourceType> levels; | |
299 | |
300 resources.reserve(body.size()); | |
301 levels.reserve(body.size()); | |
302 | |
303 const Json::Value& arr = body[KEY_RESOURCES]; | |
304 for (Json::Value::ArrayIndex i = 0; i < arr.size(); i++) | |
305 { | |
306 if (arr[i].type() != Json::arrayValue || | |
307 arr[i].size() != 2u || | |
308 arr[i][0].type() != Json::stringValue || | |
309 arr[i][1].type() != Json::stringValue) | |
310 { | |
311 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat, | |
312 "Each entry must be an array containing 2 strings, " | |
313 "the resource level and its ID"); | |
314 } | |
315 else | |
316 { | |
317 levels.push_back(Orthanc::StringToResourceType(arr[i][0].asCString())); | |
318 resources.push_back(arr[i][1].asString()); | |
319 } | |
320 } | |
321 | |
322 OrthancPlugins::OrthancJob::SubmitFromRestApiPost( | |
323 output, body, new LargeDeleteJob(resources, levels)); | |
324 } |