Mercurial > hg > orthanc
annotate OrthancServer/Plugins/Samples/DelayedDeletion/LargeDeleteJob.cpp @ 5264:99751c5a7cfe
added missing copyrights
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Wed, 12 Apr 2023 17:43:05 +0200 |
parents | c2ebc47f4f18 |
children | 49477780e25a |
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 |
99751c5a7cfe
added missing copyrights
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
5024
diff
changeset
|
5 * Copyright (C) 2017-2023 Osimis S.A., Belgium |
99751c5a7cfe
added missing copyrights
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
5024
diff
changeset
|
6 * Copyright (C) 2021-2023 Sebastien Jodogne, ICTEAM UCLouvain, Belgium |
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), | |
171 posDelete_(0) | |
172 { | |
173 if (resources.size() != levels.size()) | |
174 { | |
175 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); | |
176 } | |
177 } | |
178 | |
179 | |
180 OrthancPluginJobStepStatus LargeDeleteJob::Step() | |
181 { | |
182 if (posResources_ == 0) | |
183 { | |
184 if (resources_.size() == 1) | |
185 { | |
186 // LOG(WARNING) << "LargeDeleteJob has started on resource: " << resources_[0]; | |
187 } | |
188 else | |
189 { | |
190 // LOG(WARNING) << "LargeDeleteJob has started"; | |
191 } | |
192 } | |
193 | |
194 if (posResources_ < resources_.size()) | |
195 { | |
196 // First step: Discovering all the instances of the resources | |
197 | |
198 ScheduleResource(levels_[posResources_], resources_[posResources_]); | |
199 | |
200 posResources_ += 1; | |
201 UpdateDeleteProgress(); | |
202 return OrthancPluginJobStepStatus_Continue; | |
203 } | |
204 else if (posInstances_ < instances_.size()) | |
205 { | |
206 // Second step: Deleting the instances one by one | |
207 | |
208 DeleteResource(Orthanc::ResourceType_Instance, instances_[posInstances_]); | |
209 | |
210 posInstances_ += 1; | |
211 UpdateDeleteProgress(); | |
212 return OrthancPluginJobStepStatus_Continue; | |
213 } | |
214 else if (posSeries_ < series_.size()) | |
215 { | |
216 // Third step: Deleting the series one by one | |
217 | |
218 DeleteResource(Orthanc::ResourceType_Series, series_[posSeries_]); | |
219 | |
220 posSeries_ += 1; | |
221 UpdateDeleteProgress(); | |
222 return OrthancPluginJobStepStatus_Continue; | |
223 } | |
224 else if (posDelete_ < resources_.size()) | |
225 { | |
226 // Fourth step: Make sure the resources where fully deleted | |
227 // (instances might have been received since the beginning of | |
228 // the job) | |
229 | |
230 DeleteResource(levels_[posDelete_], resources_[posDelete_]); | |
231 | |
232 posDelete_ += 1; | |
233 UpdateDeleteProgress(); | |
234 return OrthancPluginJobStepStatus_Continue; | |
235 } | |
236 else | |
237 { | |
238 if (resources_.size() == 1) | |
239 { | |
240 // LOG(WARNING) << "LargeDeleteJob has completed on resource: " << resources_[0]; | |
241 } | |
242 else | |
243 { | |
244 // LOG(WARNING) << "LargeDeleteJob has completed"; | |
245 } | |
246 | |
247 UpdateProgress(1); | |
248 return OrthancPluginJobStepStatus_Success; | |
249 } | |
250 } | |
251 | |
252 | |
253 void LargeDeleteJob::Reset() | |
254 { | |
255 posResources_ = 0; | |
256 posInstances_ = 0; | |
257 posDelete_ = 0; | |
258 instances_.clear(); | |
259 } | |
260 | |
261 | |
262 void LargeDeleteJob::RestHandler(OrthancPluginRestOutput* output, | |
263 const char* url, | |
264 const OrthancPluginHttpRequest* request) | |
265 { | |
266 static const char* KEY_RESOURCES = "Resources"; | |
267 | |
268 if (request->method != OrthancPluginHttpMethod_Post) | |
269 { | |
270 OrthancPluginSendMethodNotAllowed(OrthancPlugins::GetGlobalContext(), output, "POST"); | |
271 return; | |
272 } | |
273 | |
274 Json::Value body; | |
275 Json::Reader reader; | |
276 if (!reader.parse(reinterpret_cast<const char*>(request->body), | |
277 reinterpret_cast<const char*>(request->body) + request->bodySize, body)) | |
278 { | |
279 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat, "JSON body is expected"); | |
280 } | |
281 | |
282 if (body.type() != Json::objectValue) | |
283 { | |
284 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat, | |
285 "Expected a JSON object in the body"); | |
286 } | |
287 | |
288 if (!body.isMember(KEY_RESOURCES) || | |
289 body[KEY_RESOURCES].type() != Json::arrayValue) | |
290 { | |
291 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat, | |
292 "The JSON object must contain an array in \"" + | |
293 std::string(KEY_RESOURCES) + "\""); | |
294 } | |
295 | |
296 std::vector<std::string> resources; | |
297 std::vector<Orthanc::ResourceType> levels; | |
298 | |
299 resources.reserve(body.size()); | |
300 levels.reserve(body.size()); | |
301 | |
302 const Json::Value& arr = body[KEY_RESOURCES]; | |
303 for (Json::Value::ArrayIndex i = 0; i < arr.size(); i++) | |
304 { | |
305 if (arr[i].type() != Json::arrayValue || | |
306 arr[i].size() != 2u || | |
307 arr[i][0].type() != Json::stringValue || | |
308 arr[i][1].type() != Json::stringValue) | |
309 { | |
310 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat, | |
311 "Each entry must be an array containing 2 strings, " | |
312 "the resource level and its ID"); | |
313 } | |
314 else | |
315 { | |
316 levels.push_back(Orthanc::StringToResourceType(arr[i][0].asCString())); | |
317 resources.push_back(arr[i][1].asString()); | |
318 } | |
319 } | |
320 | |
321 OrthancPlugins::OrthancJob::SubmitFromRestApiPost( | |
322 output, body, new LargeDeleteJob(resources, levels)); | |
323 } |