Mercurial > hg > orthanc
comparison PalantirServer/ServerIndex.cpp @ 0:3959d33612cc
initial commit
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Thu, 19 Jul 2012 14:32:22 +0200 |
parents | |
children | 67a6978503b7 |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:3959d33612cc |
---|---|
1 /** | |
2 * Palantir - A Lightweight, RESTful DICOM Store | |
3 * Copyright (C) 2012 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 * This program is distributed in the hope that it will be useful, but | |
12 * WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
14 * General Public License for more details. | |
15 * | |
16 * You should have received a copy of the GNU General Public License | |
17 * along with this program. If not, see <http://www.gnu.org/licenses/>. | |
18 **/ | |
19 | |
20 | |
21 #include "ServerIndex.h" | |
22 | |
23 using namespace Palantir; | |
24 | |
25 #include <EmbeddedResources.h> | |
26 #include "../Core/Toolbox.h" | |
27 #include "../Core/Uuid.h" | |
28 #include "../Core/DicomFormat/DicomArray.h" | |
29 #include "../Core/SQLite/Transaction.h" | |
30 #include "FromDcmtkBridge.h" | |
31 | |
32 #include <boost/lexical_cast.hpp> | |
33 #include <stdio.h> | |
34 | |
35 namespace Palantir | |
36 { | |
37 namespace Internals | |
38 { | |
39 class DeleteFromFileStorageFunction : public SQLite::IScalarFunction | |
40 { | |
41 private: | |
42 FileStorage fileStorage_; | |
43 | |
44 public: | |
45 DeleteFromFileStorageFunction(const std::string& path) : | |
46 fileStorage_(path) | |
47 { | |
48 } | |
49 | |
50 virtual const char* GetName() const | |
51 { | |
52 return "DeleteFromFileStorage"; | |
53 } | |
54 | |
55 virtual unsigned int GetCardinality() const | |
56 { | |
57 return 1; | |
58 } | |
59 | |
60 virtual void Compute(SQLite::FunctionContext& context) | |
61 { | |
62 std::string fileUuid = context.GetStringValue(0); | |
63 printf("Removing file [%s]\n", fileUuid.c_str()); | |
64 if (Toolbox::IsUuid(fileUuid)) | |
65 { | |
66 fileStorage_.Remove(fileUuid); | |
67 } | |
68 } | |
69 }; | |
70 | |
71 | |
72 class SignalDeletedLevelFunction : public SQLite::IScalarFunction | |
73 { | |
74 private: | |
75 int remainingLevel_; | |
76 std::string remainingLevelUuid_; | |
77 | |
78 public: | |
79 void Clear() | |
80 { | |
81 remainingLevel_ = std::numeric_limits<int>::max(); | |
82 } | |
83 | |
84 bool HasRemainingLevel() const | |
85 { | |
86 return (remainingLevel_ != 0 && | |
87 remainingLevel_ != std::numeric_limits<int>::max()); | |
88 } | |
89 | |
90 const std::string& GetRemainingLevelUuid() const | |
91 { | |
92 assert(HasRemainingLevel()); | |
93 return remainingLevelUuid_; | |
94 } | |
95 | |
96 const char* GetRemainingLevelType() const | |
97 { | |
98 assert(HasRemainingLevel()); | |
99 switch (remainingLevel_) | |
100 { | |
101 case 1: | |
102 return "patient"; | |
103 case 2: | |
104 return "study"; | |
105 case 3: | |
106 return "series"; | |
107 default: | |
108 throw PalantirException(ErrorCode_InternalError); | |
109 } | |
110 } | |
111 | |
112 virtual const char* GetName() const | |
113 { | |
114 return "SignalDeletedLevel"; | |
115 } | |
116 | |
117 virtual unsigned int GetCardinality() const | |
118 { | |
119 return 2; | |
120 } | |
121 | |
122 virtual void Compute(SQLite::FunctionContext& context) | |
123 { | |
124 int level = context.GetIntValue(0); | |
125 if (level < remainingLevel_) | |
126 { | |
127 remainingLevel_ = level; | |
128 remainingLevelUuid_ = context.GetStringValue(1); | |
129 } | |
130 | |
131 //printf("deleted level [%d] [%s]\n", level, context.GetStringValue(1).c_str()); | |
132 } | |
133 }; | |
134 } | |
135 | |
136 | |
137 void ServerIndex::StoreMainDicomTags(const std::string& uuid, | |
138 const DicomMap& map) | |
139 { | |
140 SQLite::Statement s(db_, SQLITE_FROM_HERE, "INSERT INTO MainDicomTags VALUES(?, ?, ?, ?)"); | |
141 | |
142 DicomArray flattened(map); | |
143 for (size_t i = 0; i < flattened.GetSize(); i++) | |
144 { | |
145 s.Reset(); | |
146 s.BindString(0, uuid); | |
147 s.BindInt(1, flattened.GetElement(i).GetTag().GetGroup()); | |
148 s.BindInt(2, flattened.GetElement(i).GetTag().GetElement()); | |
149 s.BindString(3, flattened.GetElement(i).GetValue().AsString()); | |
150 s.Run(); | |
151 } | |
152 } | |
153 | |
154 bool ServerIndex::GetMainDicomStringTag(std::string& result, | |
155 const std::string& uuid, | |
156 const DicomTag& tag) | |
157 { | |
158 SQLite::Statement s(db_, SQLITE_FROM_HERE, | |
159 "SELECT * FROM MainDicomTags WHERE uuid=? AND tagGroup=? AND tagElement=?"); | |
160 s.BindString(0, uuid); | |
161 s.BindInt(1, tag.GetGroup()); | |
162 s.BindInt(2, tag.GetElement()); | |
163 if (!s.Step()) | |
164 { | |
165 return false; | |
166 } | |
167 | |
168 result = s.ColumnString(0); | |
169 return true; | |
170 } | |
171 | |
172 bool ServerIndex::GetMainDicomIntTag(int& result, | |
173 const std::string& uuid, | |
174 const DicomTag& tag) | |
175 { | |
176 std::string s; | |
177 if (!GetMainDicomStringTag(s, uuid, tag)) | |
178 { | |
179 return false; | |
180 } | |
181 | |
182 try | |
183 { | |
184 result = boost::lexical_cast<int>(s); | |
185 return true; | |
186 } | |
187 catch (boost::bad_lexical_cast) | |
188 { | |
189 return false; | |
190 } | |
191 } | |
192 | |
193 bool ServerIndex::HasInstance(std::string& instanceUuid, | |
194 const std::string& dicomInstance) | |
195 { | |
196 SQLite::Statement s(db_, SQLITE_FROM_HERE, "SELECT uuid FROM Instances WHERE dicomInstance=?"); | |
197 s.BindString(0, dicomInstance); | |
198 if (s.Step()) | |
199 { | |
200 instanceUuid = s.ColumnString(0); | |
201 return true; | |
202 } | |
203 else | |
204 { | |
205 return false; | |
206 } | |
207 } | |
208 | |
209 | |
210 void ServerIndex::RecordChange(const std::string& resourceType, | |
211 const std::string& uuid) | |
212 { | |
213 SQLite::Statement s(db_, SQLITE_FROM_HERE, "INSERT INTO Changes VALUES(NULL, ?, ?)"); | |
214 s.BindString(0, resourceType); | |
215 s.BindString(1, uuid); | |
216 s.Run(); | |
217 } | |
218 | |
219 | |
220 std::string ServerIndex::CreateInstance(const std::string& parentSeriesUuid, | |
221 const std::string& dicomInstance, | |
222 const DicomMap& dicomSummary, | |
223 const std::string& fileUuid, | |
224 uint64_t fileSize, | |
225 const std::string& jsonUuid, | |
226 const std::string& distantAet) | |
227 { | |
228 std::string instanceUuid = Toolbox::GenerateUuid(); | |
229 | |
230 SQLite::Statement s(db_, SQLITE_FROM_HERE, "INSERT INTO Instances VALUES(?, ?, ?, ?, ?, ?, ?)"); | |
231 s.BindString(0, instanceUuid); | |
232 s.BindString(1, parentSeriesUuid); | |
233 s.BindString(2, dicomInstance); | |
234 s.BindString(3, fileUuid); | |
235 s.BindInt64(4, fileSize); | |
236 s.BindString(5, jsonUuid); | |
237 s.BindString(6, distantAet); | |
238 s.Run(); | |
239 | |
240 RecordChange("instances", instanceUuid); | |
241 | |
242 DicomMap dicom; | |
243 dicomSummary.ExtractInstanceInformation(dicom); | |
244 dicom.Remove(DicomTag::INSTANCE_UID); | |
245 StoreMainDicomTags(instanceUuid, dicom); | |
246 | |
247 return instanceUuid; | |
248 } | |
249 | |
250 void ServerIndex::RemoveInstance(const std::string& uuid) | |
251 { | |
252 SQLite::Statement s(db_, SQLITE_FROM_HERE, "DELETE FROM Instances WHERE uuid=?"); | |
253 s.BindString(0, uuid); | |
254 s.Run(); | |
255 } | |
256 | |
257 bool ServerIndex::HasSeries(std::string& seriesUuid, | |
258 const std::string& dicomSeries) | |
259 { | |
260 SQLite::Statement s(db_, SQLITE_FROM_HERE, "SELECT uuid FROM Series WHERE dicomSeries=?"); | |
261 s.BindString(0, dicomSeries); | |
262 if (s.Step()) | |
263 { | |
264 seriesUuid = s.ColumnString(0); | |
265 return true; | |
266 } | |
267 else | |
268 { | |
269 return false; | |
270 } | |
271 } | |
272 | |
273 std::string ServerIndex::CreateSeries(const std::string& parentStudyUuid, | |
274 const std::string& dicomSeries, | |
275 const DicomMap& dicomSummary) | |
276 { | |
277 std::string seriesUuid = Toolbox::GenerateUuid(); | |
278 | |
279 SQLite::Statement s(db_, SQLITE_FROM_HERE, "INSERT INTO Series VALUES(?, ?, ?)"); | |
280 s.BindString(0, seriesUuid); | |
281 s.BindString(1, parentStudyUuid); | |
282 s.BindString(2, dicomSeries); | |
283 s.Run(); | |
284 | |
285 RecordChange("series", seriesUuid); | |
286 | |
287 DicomMap dicom; | |
288 dicomSummary.ExtractSeriesInformation(dicom); | |
289 dicom.Remove(DicomTag::SERIES_UID); | |
290 StoreMainDicomTags(seriesUuid, dicom); | |
291 | |
292 return seriesUuid; | |
293 } | |
294 | |
295 bool ServerIndex::HasStudy(std::string& studyUuid, | |
296 const std::string& dicomStudy) | |
297 { | |
298 SQLite::Statement s(db_, SQLITE_FROM_HERE, "SELECT uuid FROM Studies WHERE dicomStudy=?"); | |
299 s.BindString(0, dicomStudy); | |
300 if (s.Step()) | |
301 { | |
302 studyUuid = s.ColumnString(0); | |
303 return true; | |
304 } | |
305 else | |
306 { | |
307 return false; | |
308 } | |
309 } | |
310 | |
311 std::string ServerIndex::CreateStudy(const std::string& parentPatientUuid, | |
312 const std::string& dicomStudy, | |
313 const DicomMap& dicomSummary) | |
314 { | |
315 std::string studyUuid = Toolbox::GenerateUuid(); | |
316 | |
317 SQLite::Statement s(db_, SQLITE_FROM_HERE, "INSERT INTO Studies VALUES(?, ?, ?)"); | |
318 s.BindString(0, studyUuid); | |
319 s.BindString(1, parentPatientUuid); | |
320 s.BindString(2, dicomStudy); | |
321 s.Run(); | |
322 | |
323 RecordChange("studies", studyUuid); | |
324 | |
325 DicomMap dicom; | |
326 dicomSummary.ExtractStudyInformation(dicom); | |
327 dicom.Remove(DicomTag::STUDY_UID); | |
328 StoreMainDicomTags(studyUuid, dicom); | |
329 | |
330 return studyUuid; | |
331 } | |
332 | |
333 | |
334 | |
335 bool ServerIndex::HasPatient(std::string& patientUuid, | |
336 const std::string& dicomPatientId) | |
337 { | |
338 SQLite::Statement s(db_, SQLITE_FROM_HERE, "SELECT uuid FROM Patients WHERE dicomPatientId=?"); | |
339 s.BindString(0, dicomPatientId); | |
340 if (s.Step()) | |
341 { | |
342 patientUuid = s.ColumnString(0); | |
343 return true; | |
344 } | |
345 else | |
346 { | |
347 return false; | |
348 } | |
349 } | |
350 | |
351 std::string ServerIndex::CreatePatient(const std::string& patientId, | |
352 const DicomMap& dicomSummary) | |
353 { | |
354 std::string patientUuid = Toolbox::GenerateUuid(); | |
355 std::string dicomPatientId = dicomSummary.GetValue(DicomTag::PATIENT_ID).AsString(); | |
356 | |
357 SQLite::Statement s(db_, SQLITE_FROM_HERE, "INSERT INTO Patients VALUES(?, ?)"); | |
358 s.BindString(0, patientUuid); | |
359 s.BindString(1, dicomPatientId); | |
360 s.Run(); | |
361 | |
362 RecordChange("patients", patientUuid); | |
363 | |
364 DicomMap dicom; | |
365 dicomSummary.ExtractPatientInformation(dicom); | |
366 dicom.Remove(DicomTag::PATIENT_ID); | |
367 StoreMainDicomTags(patientUuid, dicom); | |
368 | |
369 return patientUuid; | |
370 } | |
371 | |
372 | |
373 void ServerIndex::GetMainDicomTags(DicomMap& map, | |
374 const std::string& uuid) | |
375 { | |
376 map.Clear(); | |
377 | |
378 SQLite::Statement s(db_, SQLITE_FROM_HERE, "SELECT * FROM MainDicomTags WHERE uuid=?"); | |
379 s.BindString(0, uuid); | |
380 while (s.Step()) | |
381 { | |
382 map.SetValue(s.ColumnInt(1), | |
383 s.ColumnInt(2), | |
384 s.ColumnString(3)); | |
385 } | |
386 } | |
387 | |
388 void ServerIndex::MainDicomTagsToJson(Json::Value& target, | |
389 const std::string& uuid) | |
390 { | |
391 DicomMap map; | |
392 GetMainDicomTags(map, uuid); | |
393 target["MainDicomTags"] = Json::objectValue; | |
394 FromDcmtkBridge::ToJson(target["MainDicomTags"], map); | |
395 } | |
396 | |
397 | |
398 bool ServerIndex::DeleteInternal(Json::Value& target, | |
399 const std::string& uuid, | |
400 const std::string& tableName) | |
401 { | |
402 boost::mutex::scoped_lock scoped_lock(mutex_); | |
403 | |
404 deletedLevels_->Clear(); | |
405 | |
406 SQLite::Statement s(db_, "DELETE FROM " + tableName + " WHERE uuid=?"); | |
407 s.BindString(0, uuid); | |
408 | |
409 if (!s.Run()) | |
410 { | |
411 return false; | |
412 } | |
413 | |
414 if (db_.GetLastChangeCount() == 0) | |
415 { | |
416 // Nothing was deleted, inexistent UUID | |
417 return false; | |
418 } | |
419 | |
420 if (deletedLevels_->HasRemainingLevel()) | |
421 { | |
422 std::string type(deletedLevels_->GetRemainingLevelType()); | |
423 const std::string& uuid = deletedLevels_->GetRemainingLevelUuid(); | |
424 | |
425 target["RemainingAncestor"] = Json::Value(Json::objectValue); | |
426 target["RemainingAncestor"]["Path"] = "/" + type + "/" + uuid; | |
427 target["RemainingAncestor"]["Type"] = type; | |
428 target["RemainingAncestor"]["ID"] = uuid; | |
429 } | |
430 else | |
431 { | |
432 target["RemainingAncestor"] = Json::nullValue; | |
433 } | |
434 | |
435 return true; | |
436 } | |
437 | |
438 | |
439 ServerIndex::ServerIndex(const std::string& storagePath) | |
440 { | |
441 boost::filesystem::path p = storagePath; | |
442 | |
443 try | |
444 { | |
445 boost::filesystem::create_directories(storagePath); | |
446 } | |
447 catch (boost::filesystem::filesystem_error) | |
448 { | |
449 } | |
450 | |
451 p /= "index"; | |
452 db_.Open(p.string()); | |
453 db_.Register(new Internals::DeleteFromFileStorageFunction(storagePath)); | |
454 deletedLevels_ = (Internals::SignalDeletedLevelFunction*) | |
455 db_.Register(new Internals::SignalDeletedLevelFunction); | |
456 | |
457 if (!db_.DoesTableExist("GlobalProperties")) | |
458 { | |
459 printf("Creating the database\n"); | |
460 std::string query; | |
461 EmbeddedResources::GetFileResource(query, EmbeddedResources::PREPARE_DATABASE); | |
462 db_.Execute(query); | |
463 } | |
464 } | |
465 | |
466 | |
467 StoreStatus ServerIndex::Store(std::string& instanceUuid, | |
468 const DicomMap& dicomSummary, | |
469 const std::string& fileUuid, | |
470 uint64_t uncompressedFileSize, | |
471 const std::string& jsonUuid, | |
472 const std::string& distantAet) | |
473 { | |
474 boost::mutex::scoped_lock scoped_lock(mutex_); | |
475 | |
476 std::string dicomPatientId = dicomSummary.GetValue(DicomTag::PATIENT_ID).AsString(); | |
477 std::string dicomInstance = dicomSummary.GetValue(DicomTag::INSTANCE_UID).AsString(); | |
478 std::string dicomSeries = dicomSummary.GetValue(DicomTag::SERIES_UID).AsString(); | |
479 std::string dicomStudy = dicomSummary.GetValue(DicomTag::STUDY_UID).AsString(); | |
480 | |
481 try | |
482 { | |
483 SQLite::Transaction t(db_); | |
484 t.Begin(); | |
485 | |
486 if (HasInstance(instanceUuid, dicomInstance)) | |
487 { | |
488 return StoreStatus_AlreadyStored; | |
489 // TODO: Check consistency? | |
490 } | |
491 | |
492 std::string patientUuid; | |
493 if (HasPatient(patientUuid, dicomPatientId)) | |
494 { | |
495 // TODO: Check consistency? | |
496 } | |
497 else | |
498 { | |
499 patientUuid = CreatePatient(dicomPatientId, dicomSummary); | |
500 } | |
501 | |
502 std::string studyUuid; | |
503 if (HasStudy(studyUuid, dicomStudy)) | |
504 { | |
505 // TODO: Check consistency? | |
506 } | |
507 else | |
508 { | |
509 studyUuid = CreateStudy(patientUuid, dicomStudy, dicomSummary); | |
510 } | |
511 | |
512 std::string seriesUuid; | |
513 if (HasSeries(seriesUuid, dicomSeries)) | |
514 { | |
515 // TODO: Check consistency? | |
516 } | |
517 else | |
518 { | |
519 seriesUuid = CreateSeries(studyUuid, dicomSeries, dicomSummary); | |
520 } | |
521 | |
522 instanceUuid = CreateInstance(seriesUuid, dicomInstance, dicomSummary, fileUuid, | |
523 uncompressedFileSize, jsonUuid, distantAet); | |
524 | |
525 t.Commit(); | |
526 return StoreStatus_Success; | |
527 //t.Rollback(); | |
528 } | |
529 catch (PalantirException& e) | |
530 { | |
531 std::cout << "EXCEPT2 [" << e.What() << "]" << " " << db_.GetErrorMessage() << std::endl; | |
532 } | |
533 | |
534 return StoreStatus_Failure; | |
535 } | |
536 | |
537 | |
538 StoreStatus ServerIndex::Store(std::string& instanceUuid, | |
539 FileStorage& storage, | |
540 const char* dicomFile, | |
541 size_t dicomSize, | |
542 const DicomMap& dicomSummary, | |
543 const Json::Value& dicomJson, | |
544 const std::string& distantAet) | |
545 { | |
546 std::string fileUuid = storage.Create(dicomFile, dicomSize); | |
547 std::string jsonUuid = storage.Create(dicomJson.toStyledString()); | |
548 StoreStatus status = Store(instanceUuid, dicomSummary, fileUuid, | |
549 dicomSize, jsonUuid, distantAet); | |
550 | |
551 if (status != StoreStatus_Success) | |
552 { | |
553 storage.Remove(fileUuid); | |
554 storage.Remove(jsonUuid); | |
555 } | |
556 | |
557 switch (status) | |
558 { | |
559 case StoreStatus_Success: | |
560 std::cout << "New instance stored: " << GetTotalSize() << std::endl; | |
561 break; | |
562 | |
563 case StoreStatus_AlreadyStored: | |
564 std::cout << "Already stored" << std::endl; | |
565 break; | |
566 | |
567 case StoreStatus_Failure: | |
568 std::cout << "Store failure" << std::endl; | |
569 break; | |
570 } | |
571 | |
572 return status; | |
573 } | |
574 | |
575 uint64_t ServerIndex::GetTotalSize() | |
576 { | |
577 boost::mutex::scoped_lock scoped_lock(mutex_); | |
578 SQLite::Statement s(db_, SQLITE_FROM_HERE, "SELECT SUM(fileSize) FROM Instances"); | |
579 s.Run(); | |
580 return s.ColumnInt64(0); | |
581 } | |
582 | |
583 | |
584 SeriesStatus ServerIndex::GetSeriesStatus(const std::string& seriesUuid) | |
585 { | |
586 int numberOfSlices; | |
587 if (!GetMainDicomIntTag(numberOfSlices, seriesUuid, DicomTag::NUMBER_OF_SLICES) || | |
588 numberOfSlices < 0) | |
589 { | |
590 return SeriesStatus_Unknown; | |
591 } | |
592 | |
593 // Loop over the instances of the series | |
594 //std::set< | |
595 | |
596 // TODO | |
597 return SeriesStatus_Unknown; | |
598 } | |
599 | |
600 | |
601 | |
602 | |
603 | |
604 bool ServerIndex::GetInstance(Json::Value& result, | |
605 const std::string& instanceUuid) | |
606 { | |
607 assert(result.type() == Json::objectValue); | |
608 boost::mutex::scoped_lock scoped_lock(mutex_); | |
609 | |
610 SQLite::Statement s(db_, SQLITE_FROM_HERE, "SELECT parentSeries, dicomInstance, fileSize, fileUuid FROM Instances WHERE uuid=?"); | |
611 s.BindString(0, instanceUuid); | |
612 if (!s.Step()) | |
613 { | |
614 return false; | |
615 } | |
616 else | |
617 { | |
618 result["ID"] = instanceUuid; | |
619 result["ParentSeries"] = s.ColumnString(0); | |
620 result["DicomSOPInstanceUID"] = s.ColumnString(1); | |
621 result["FileSize"] = s.ColumnInt(2); // TODO switch to 64bit with JsonCpp 0.6? | |
622 result["FileUuid"] = s.ColumnString(3); | |
623 MainDicomTagsToJson(result, instanceUuid); | |
624 return true; | |
625 } | |
626 } | |
627 | |
628 | |
629 bool ServerIndex::GetSeries(Json::Value& result, | |
630 const std::string& seriesUuid) | |
631 { | |
632 assert(result.type() == Json::objectValue); | |
633 boost::mutex::scoped_lock scoped_lock(mutex_); | |
634 | |
635 SQLite::Statement s1(db_, SQLITE_FROM_HERE, "SELECT parentStudy, dicomSeries FROM Series WHERE uuid=?"); | |
636 s1.BindString(0, seriesUuid); | |
637 if (!s1.Step()) | |
638 { | |
639 return false; | |
640 } | |
641 | |
642 result["ID"] = seriesUuid; | |
643 result["ParentStudy"] = s1.ColumnString(0); | |
644 result["DicomSeriesInstanceUID"] = s1.ColumnString(1); | |
645 MainDicomTagsToJson(result, seriesUuid); | |
646 | |
647 Json::Value instances(Json::arrayValue); | |
648 SQLite::Statement s2(db_, SQLITE_FROM_HERE, "SELECT uuid FROM Instances WHERE parentSeries=?"); | |
649 s2.BindString(0, seriesUuid); | |
650 while (s2.Step()) | |
651 { | |
652 instances.append(s2.ColumnString(0)); | |
653 } | |
654 | |
655 result["Instances"] = instances; | |
656 | |
657 return true; | |
658 } | |
659 | |
660 | |
661 bool ServerIndex::GetStudy(Json::Value& result, | |
662 const std::string& studyUuid) | |
663 { | |
664 assert(result.type() == Json::objectValue); | |
665 boost::mutex::scoped_lock scoped_lock(mutex_); | |
666 | |
667 SQLite::Statement s1(db_, SQLITE_FROM_HERE, "SELECT parentPatient, dicomStudy FROM Studies WHERE uuid=?"); | |
668 s1.BindString(0, studyUuid); | |
669 if (!s1.Step()) | |
670 { | |
671 return false; | |
672 } | |
673 | |
674 result["ID"] = studyUuid; | |
675 result["ParentPatient"] = s1.ColumnString(0); | |
676 result["DicomStudyInstanceUID"] = s1.ColumnString(1); | |
677 MainDicomTagsToJson(result, studyUuid); | |
678 | |
679 Json::Value series(Json::arrayValue); | |
680 SQLite::Statement s2(db_, SQLITE_FROM_HERE, "SELECT uuid FROM Series WHERE parentStudy=?"); | |
681 s2.BindString(0, studyUuid); | |
682 while (s2.Step()) | |
683 { | |
684 series.append(s2.ColumnString(0)); | |
685 } | |
686 | |
687 result["Series"] = series; | |
688 return true; | |
689 } | |
690 | |
691 | |
692 bool ServerIndex::GetPatient(Json::Value& result, | |
693 const std::string& patientUuid) | |
694 { | |
695 assert(result.type() == Json::objectValue); | |
696 boost::mutex::scoped_lock scoped_lock(mutex_); | |
697 | |
698 SQLite::Statement s1(db_, SQLITE_FROM_HERE, "SELECT dicomPatientId FROM Patients WHERE uuid=?"); | |
699 s1.BindString(0, patientUuid); | |
700 if (!s1.Step()) | |
701 { | |
702 return false; | |
703 } | |
704 | |
705 result["ID"] = patientUuid; | |
706 result["DicomPatientID"] = s1.ColumnString(0); | |
707 MainDicomTagsToJson(result, patientUuid); | |
708 | |
709 Json::Value studies(Json::arrayValue); | |
710 SQLite::Statement s2(db_, SQLITE_FROM_HERE, "SELECT uuid FROM Studies WHERE parentPatient=?"); | |
711 s2.BindString(0, patientUuid); | |
712 while (s2.Step()) | |
713 { | |
714 studies.append(s2.ColumnString(0)); | |
715 } | |
716 | |
717 result["Studies"] = studies; | |
718 return true; | |
719 } | |
720 | |
721 | |
722 bool ServerIndex::GetJsonFile(std::string& fileUuid, | |
723 const std::string& instanceUuid) | |
724 { | |
725 boost::mutex::scoped_lock scoped_lock(mutex_); | |
726 | |
727 SQLite::Statement s(db_, SQLITE_FROM_HERE, "SELECT jsonUuid FROM Instances WHERE uuid=?"); | |
728 s.BindString(0, instanceUuid); | |
729 if (s.Step()) | |
730 { | |
731 fileUuid = s.ColumnString(0); | |
732 return true; | |
733 } | |
734 else | |
735 { | |
736 return false; | |
737 } | |
738 } | |
739 | |
740 bool ServerIndex::GetDicomFile(std::string& fileUuid, | |
741 const std::string& instanceUuid) | |
742 { | |
743 boost::mutex::scoped_lock scoped_lock(mutex_); | |
744 | |
745 SQLite::Statement s(db_, SQLITE_FROM_HERE, "SELECT fileUuid FROM Instances WHERE uuid=?"); | |
746 s.BindString(0, instanceUuid); | |
747 if (s.Step()) | |
748 { | |
749 fileUuid = s.ColumnString(0); | |
750 return true; | |
751 } | |
752 else | |
753 { | |
754 return false; | |
755 } | |
756 } | |
757 | |
758 | |
759 void ServerIndex::GetAllUuids(Json::Value& target, | |
760 const std::string& tableName) | |
761 { | |
762 assert(target.type() == Json::arrayValue); | |
763 boost::mutex::scoped_lock scoped_lock(mutex_); | |
764 | |
765 std::string query = "SELECT uuid FROM " + tableName; | |
766 SQLite::Statement s(db_, query); | |
767 while (s.Step()) | |
768 { | |
769 target.append(s.ColumnString(0)); | |
770 } | |
771 } | |
772 | |
773 | |
774 bool ServerIndex::GetChanges(Json::Value& target, | |
775 int64_t since, | |
776 const std::string& filter, | |
777 unsigned int maxResults) | |
778 { | |
779 assert(target.type() == Json::objectValue); | |
780 boost::mutex::scoped_lock scoped_lock(mutex_); | |
781 | |
782 if (filter.size() != 0 && | |
783 filter != "instances" && | |
784 filter != "series" && | |
785 filter != "studies" && | |
786 filter != "patients") | |
787 { | |
788 return false; | |
789 } | |
790 | |
791 std::auto_ptr<SQLite::Statement> s; | |
792 if (filter.size() == 0) | |
793 { | |
794 s.reset(new SQLite::Statement(db_, SQLITE_FROM_HERE, "SELECT * FROM Changes WHERE seq>? " | |
795 "ORDER BY seq LIMIT ?")); | |
796 s->BindInt64(0, since); | |
797 s->BindInt(1, maxResults); | |
798 } | |
799 else | |
800 { | |
801 s.reset(new SQLite::Statement(db_, SQLITE_FROM_HERE, "SELECT * FROM Changes WHERE seq>? " | |
802 "AND basePath=? ORDER BY seq LIMIT ?")); | |
803 s->BindInt64(0, since); | |
804 s->BindString(1, filter); | |
805 s->BindInt(2, maxResults); | |
806 } | |
807 | |
808 int64_t lastSeq = 0; | |
809 Json::Value results(Json::arrayValue); | |
810 while (s->Step()) | |
811 { | |
812 int64_t seq = s->ColumnInt64(0); | |
813 std::string basePath = s->ColumnString(1); | |
814 std::string uuid = s->ColumnString(2); | |
815 | |
816 if (filter.size() == 0 || | |
817 filter == basePath) | |
818 { | |
819 Json::Value change(Json::objectValue); | |
820 change["Seq"] = static_cast<int>(seq); // TODO JsonCpp in 64bit | |
821 change["BasePath"] = basePath; | |
822 change["ID"] = uuid; | |
823 results.append(change); | |
824 } | |
825 | |
826 if (seq > lastSeq) | |
827 { | |
828 lastSeq = seq; | |
829 } | |
830 } | |
831 | |
832 target["Results"] = results; | |
833 target["LastSeq"] = static_cast<int>(lastSeq); // TODO JsonCpp in 64bit | |
834 | |
835 return true; | |
836 } | |
837 } |