Mercurial > hg > orthanc
comparison OrthancServer/ServerIndex.cpp @ 57:4bc019d2f969 orthanc-renaming
renaming
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Sun, 16 Sep 2012 09:22:48 +0200 |
parents | PalanthirServer/ServerIndex.cpp@a15e90e5d6fc |
children | a70bb32802ae |
comparison
equal
deleted
inserted
replaced
56:088c4f23e2c8 | 57:4bc019d2f969 |
---|---|
1 /** | |
2 * Palanthir - 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 Palanthir; | |
24 | |
25 #ifndef NOMINMAX | |
26 #define NOMINMAX | |
27 #endif | |
28 | |
29 #include "EmbeddedResources.h" | |
30 #include "../Core/Toolbox.h" | |
31 #include "../Core/Uuid.h" | |
32 #include "../Core/DicomFormat/DicomArray.h" | |
33 #include "../Core/SQLite/Transaction.h" | |
34 #include "FromDcmtkBridge.h" | |
35 | |
36 #include <boost/lexical_cast.hpp> | |
37 #include <stdio.h> | |
38 | |
39 namespace Palanthir | |
40 { | |
41 namespace Internals | |
42 { | |
43 class DeleteFromFileStorageFunction : public SQLite::IScalarFunction | |
44 { | |
45 private: | |
46 FileStorage fileStorage_; | |
47 | |
48 public: | |
49 DeleteFromFileStorageFunction(const std::string& path) : | |
50 fileStorage_(path) | |
51 { | |
52 } | |
53 | |
54 virtual const char* GetName() const | |
55 { | |
56 return "DeleteFromFileStorage"; | |
57 } | |
58 | |
59 virtual unsigned int GetCardinality() const | |
60 { | |
61 return 1; | |
62 } | |
63 | |
64 virtual void Compute(SQLite::FunctionContext& context) | |
65 { | |
66 std::string fileUuid = context.GetStringValue(0); | |
67 printf("Removing file [%s]\n", fileUuid.c_str()); | |
68 if (Toolbox::IsUuid(fileUuid)) | |
69 { | |
70 fileStorage_.Remove(fileUuid); | |
71 } | |
72 } | |
73 }; | |
74 | |
75 | |
76 class SignalDeletedLevelFunction : public SQLite::IScalarFunction | |
77 { | |
78 private: | |
79 int remainingLevel_; | |
80 std::string remainingLevelUuid_; | |
81 | |
82 public: | |
83 void Clear() | |
84 { | |
85 remainingLevel_ = std::numeric_limits<int>::max(); | |
86 } | |
87 | |
88 bool HasRemainingLevel() const | |
89 { | |
90 return (remainingLevel_ != 0 && | |
91 remainingLevel_ != std::numeric_limits<int>::max()); | |
92 } | |
93 | |
94 const std::string& GetRemainingLevelUuid() const | |
95 { | |
96 assert(HasRemainingLevel()); | |
97 return remainingLevelUuid_; | |
98 } | |
99 | |
100 const char* GetRemainingLevelType() const | |
101 { | |
102 assert(HasRemainingLevel()); | |
103 switch (remainingLevel_) | |
104 { | |
105 case 1: | |
106 return "patient"; | |
107 case 2: | |
108 return "study"; | |
109 case 3: | |
110 return "series"; | |
111 default: | |
112 throw PalanthirException(ErrorCode_InternalError); | |
113 } | |
114 } | |
115 | |
116 virtual const char* GetName() const | |
117 { | |
118 return "SignalDeletedLevel"; | |
119 } | |
120 | |
121 virtual unsigned int GetCardinality() const | |
122 { | |
123 return 2; | |
124 } | |
125 | |
126 virtual void Compute(SQLite::FunctionContext& context) | |
127 { | |
128 int level = context.GetIntValue(0); | |
129 if (level < remainingLevel_) | |
130 { | |
131 remainingLevel_ = level; | |
132 remainingLevelUuid_ = context.GetStringValue(1); | |
133 } | |
134 | |
135 //printf("deleted level [%d] [%s]\n", level, context.GetStringValue(1).c_str()); | |
136 } | |
137 }; | |
138 } | |
139 | |
140 | |
141 void ServerIndex::StoreMainDicomTags(const std::string& uuid, | |
142 const DicomMap& map) | |
143 { | |
144 SQLite::Statement s(db_, SQLITE_FROM_HERE, "INSERT INTO MainDicomTags VALUES(?, ?, ?, ?)"); | |
145 | |
146 DicomArray flattened(map); | |
147 for (size_t i = 0; i < flattened.GetSize(); i++) | |
148 { | |
149 s.Reset(); | |
150 s.BindString(0, uuid); | |
151 s.BindInt(1, flattened.GetElement(i).GetTag().GetGroup()); | |
152 s.BindInt(2, flattened.GetElement(i).GetTag().GetElement()); | |
153 s.BindString(3, flattened.GetElement(i).GetValue().AsString()); | |
154 s.Run(); | |
155 } | |
156 } | |
157 | |
158 bool ServerIndex::GetMainDicomStringTag(std::string& result, | |
159 const std::string& uuid, | |
160 const DicomTag& tag) | |
161 { | |
162 SQLite::Statement s(db_, SQLITE_FROM_HERE, | |
163 "SELECT * FROM MainDicomTags WHERE uuid=? AND tagGroup=? AND tagElement=?"); | |
164 s.BindString(0, uuid); | |
165 s.BindInt(1, tag.GetGroup()); | |
166 s.BindInt(2, tag.GetElement()); | |
167 if (!s.Step()) | |
168 { | |
169 return false; | |
170 } | |
171 | |
172 result = s.ColumnString(0); | |
173 return true; | |
174 } | |
175 | |
176 bool ServerIndex::GetMainDicomIntTag(int& result, | |
177 const std::string& uuid, | |
178 const DicomTag& tag) | |
179 { | |
180 std::string s; | |
181 if (!GetMainDicomStringTag(s, uuid, tag)) | |
182 { | |
183 return false; | |
184 } | |
185 | |
186 try | |
187 { | |
188 result = boost::lexical_cast<int>(s); | |
189 return true; | |
190 } | |
191 catch (boost::bad_lexical_cast) | |
192 { | |
193 return false; | |
194 } | |
195 } | |
196 | |
197 bool ServerIndex::HasInstance(std::string& instanceUuid, | |
198 const std::string& dicomInstance) | |
199 { | |
200 SQLite::Statement s(db_, SQLITE_FROM_HERE, "SELECT uuid FROM Instances WHERE dicomInstance=?"); | |
201 s.BindString(0, dicomInstance); | |
202 if (s.Step()) | |
203 { | |
204 instanceUuid = s.ColumnString(0); | |
205 return true; | |
206 } | |
207 else | |
208 { | |
209 return false; | |
210 } | |
211 } | |
212 | |
213 | |
214 void ServerIndex::RecordChange(const std::string& resourceType, | |
215 const std::string& uuid) | |
216 { | |
217 SQLite::Statement s(db_, SQLITE_FROM_HERE, "INSERT INTO Changes VALUES(NULL, ?, ?)"); | |
218 s.BindString(0, resourceType); | |
219 s.BindString(1, uuid); | |
220 s.Run(); | |
221 } | |
222 | |
223 | |
224 std::string ServerIndex::CreateInstance(const std::string& parentSeriesUuid, | |
225 const std::string& dicomInstance, | |
226 const DicomMap& dicomSummary, | |
227 const std::string& fileUuid, | |
228 uint64_t fileSize, | |
229 const std::string& jsonUuid, | |
230 const std::string& distantAet) | |
231 { | |
232 std::string instanceUuid = Toolbox::GenerateUuid(); | |
233 | |
234 SQLite::Statement s(db_, SQLITE_FROM_HERE, "INSERT INTO Instances VALUES(?, ?, ?, ?, ?, ?, ?)"); | |
235 s.BindString(0, instanceUuid); | |
236 s.BindString(1, parentSeriesUuid); | |
237 s.BindString(2, dicomInstance); | |
238 s.BindString(3, fileUuid); | |
239 s.BindInt64(4, fileSize); | |
240 s.BindString(5, jsonUuid); | |
241 s.BindString(6, distantAet); | |
242 s.Run(); | |
243 | |
244 RecordChange("instances", instanceUuid); | |
245 | |
246 DicomMap dicom; | |
247 dicomSummary.ExtractInstanceInformation(dicom); | |
248 StoreMainDicomTags(instanceUuid, dicom); | |
249 | |
250 return instanceUuid; | |
251 } | |
252 | |
253 void ServerIndex::RemoveInstance(const std::string& uuid) | |
254 { | |
255 SQLite::Statement s(db_, SQLITE_FROM_HERE, "DELETE FROM Instances WHERE uuid=?"); | |
256 s.BindString(0, uuid); | |
257 s.Run(); | |
258 } | |
259 | |
260 bool ServerIndex::HasSeries(std::string& seriesUuid, | |
261 const std::string& dicomSeries) | |
262 { | |
263 SQLite::Statement s(db_, SQLITE_FROM_HERE, "SELECT uuid FROM Series WHERE dicomSeries=?"); | |
264 s.BindString(0, dicomSeries); | |
265 if (s.Step()) | |
266 { | |
267 seriesUuid = s.ColumnString(0); | |
268 return true; | |
269 } | |
270 else | |
271 { | |
272 return false; | |
273 } | |
274 } | |
275 | |
276 std::string ServerIndex::CreateSeries(const std::string& parentStudyUuid, | |
277 const std::string& dicomSeries, | |
278 const DicomMap& dicomSummary) | |
279 { | |
280 std::string seriesUuid = Toolbox::GenerateUuid(); | |
281 | |
282 SQLite::Statement s(db_, SQLITE_FROM_HERE, "INSERT INTO Series VALUES(?, ?, ?)"); | |
283 s.BindString(0, seriesUuid); | |
284 s.BindString(1, parentStudyUuid); | |
285 s.BindString(2, dicomSeries); | |
286 s.Run(); | |
287 | |
288 RecordChange("series", seriesUuid); | |
289 | |
290 DicomMap dicom; | |
291 dicomSummary.ExtractSeriesInformation(dicom); | |
292 StoreMainDicomTags(seriesUuid, dicom); | |
293 | |
294 return seriesUuid; | |
295 } | |
296 | |
297 bool ServerIndex::HasStudy(std::string& studyUuid, | |
298 const std::string& dicomStudy) | |
299 { | |
300 SQLite::Statement s(db_, SQLITE_FROM_HERE, "SELECT uuid FROM Studies WHERE dicomStudy=?"); | |
301 s.BindString(0, dicomStudy); | |
302 if (s.Step()) | |
303 { | |
304 studyUuid = s.ColumnString(0); | |
305 return true; | |
306 } | |
307 else | |
308 { | |
309 return false; | |
310 } | |
311 } | |
312 | |
313 std::string ServerIndex::CreateStudy(const std::string& parentPatientUuid, | |
314 const std::string& dicomStudy, | |
315 const DicomMap& dicomSummary) | |
316 { | |
317 std::string studyUuid = Toolbox::GenerateUuid(); | |
318 | |
319 SQLite::Statement s(db_, SQLITE_FROM_HERE, "INSERT INTO Studies VALUES(?, ?, ?)"); | |
320 s.BindString(0, studyUuid); | |
321 s.BindString(1, parentPatientUuid); | |
322 s.BindString(2, dicomStudy); | |
323 s.Run(); | |
324 | |
325 RecordChange("studies", studyUuid); | |
326 | |
327 DicomMap dicom; | |
328 dicomSummary.ExtractStudyInformation(dicom); | |
329 StoreMainDicomTags(studyUuid, dicom); | |
330 | |
331 return studyUuid; | |
332 } | |
333 | |
334 | |
335 | |
336 bool ServerIndex::HasPatient(std::string& patientUuid, | |
337 const std::string& dicomPatientId) | |
338 { | |
339 SQLite::Statement s(db_, SQLITE_FROM_HERE, "SELECT uuid FROM Patients WHERE dicomPatientId=?"); | |
340 s.BindString(0, dicomPatientId); | |
341 if (s.Step()) | |
342 { | |
343 patientUuid = s.ColumnString(0); | |
344 return true; | |
345 } | |
346 else | |
347 { | |
348 return false; | |
349 } | |
350 } | |
351 | |
352 std::string ServerIndex::CreatePatient(const std::string& patientId, | |
353 const DicomMap& dicomSummary) | |
354 { | |
355 std::string patientUuid = Toolbox::GenerateUuid(); | |
356 std::string dicomPatientId = dicomSummary.GetValue(DicomTag::PATIENT_ID).AsString(); | |
357 | |
358 SQLite::Statement s(db_, SQLITE_FROM_HERE, "INSERT INTO Patients VALUES(?, ?)"); | |
359 s.BindString(0, patientUuid); | |
360 s.BindString(1, dicomPatientId); | |
361 s.Run(); | |
362 | |
363 RecordChange("patients", patientUuid); | |
364 | |
365 DicomMap dicom; | |
366 dicomSummary.ExtractPatientInformation(dicom); | |
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 (PalanthirException& 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["FileSize"] = s.ColumnInt(2); // TODO switch to 64bit with JsonCpp 0.6? | |
621 result["FileUuid"] = s.ColumnString(3); | |
622 MainDicomTagsToJson(result, instanceUuid); | |
623 return true; | |
624 } | |
625 } | |
626 | |
627 | |
628 bool ServerIndex::GetSeries(Json::Value& result, | |
629 const std::string& seriesUuid) | |
630 { | |
631 assert(result.type() == Json::objectValue); | |
632 boost::mutex::scoped_lock scoped_lock(mutex_); | |
633 | |
634 SQLite::Statement s1(db_, SQLITE_FROM_HERE, "SELECT parentStudy, dicomSeries FROM Series WHERE uuid=?"); | |
635 s1.BindString(0, seriesUuid); | |
636 if (!s1.Step()) | |
637 { | |
638 return false; | |
639 } | |
640 | |
641 result["ID"] = seriesUuid; | |
642 result["ParentStudy"] = s1.ColumnString(0); | |
643 MainDicomTagsToJson(result, seriesUuid); | |
644 | |
645 Json::Value instances(Json::arrayValue); | |
646 SQLite::Statement s2(db_, SQLITE_FROM_HERE, "SELECT uuid FROM Instances WHERE parentSeries=?"); | |
647 s2.BindString(0, seriesUuid); | |
648 while (s2.Step()) | |
649 { | |
650 instances.append(s2.ColumnString(0)); | |
651 } | |
652 | |
653 result["Instances"] = instances; | |
654 | |
655 return true; | |
656 } | |
657 | |
658 | |
659 bool ServerIndex::GetStudy(Json::Value& result, | |
660 const std::string& studyUuid) | |
661 { | |
662 assert(result.type() == Json::objectValue); | |
663 boost::mutex::scoped_lock scoped_lock(mutex_); | |
664 | |
665 SQLite::Statement s1(db_, SQLITE_FROM_HERE, "SELECT parentPatient, dicomStudy FROM Studies WHERE uuid=?"); | |
666 s1.BindString(0, studyUuid); | |
667 if (!s1.Step()) | |
668 { | |
669 return false; | |
670 } | |
671 | |
672 result["ID"] = studyUuid; | |
673 result["ParentPatient"] = s1.ColumnString(0); | |
674 MainDicomTagsToJson(result, studyUuid); | |
675 | |
676 Json::Value series(Json::arrayValue); | |
677 SQLite::Statement s2(db_, SQLITE_FROM_HERE, "SELECT uuid FROM Series WHERE parentStudy=?"); | |
678 s2.BindString(0, studyUuid); | |
679 while (s2.Step()) | |
680 { | |
681 series.append(s2.ColumnString(0)); | |
682 } | |
683 | |
684 result["Series"] = series; | |
685 return true; | |
686 } | |
687 | |
688 | |
689 bool ServerIndex::GetPatient(Json::Value& result, | |
690 const std::string& patientUuid) | |
691 { | |
692 assert(result.type() == Json::objectValue); | |
693 boost::mutex::scoped_lock scoped_lock(mutex_); | |
694 | |
695 SQLite::Statement s1(db_, SQLITE_FROM_HERE, "SELECT dicomPatientId FROM Patients WHERE uuid=?"); | |
696 s1.BindString(0, patientUuid); | |
697 if (!s1.Step()) | |
698 { | |
699 return false; | |
700 } | |
701 | |
702 result["ID"] = patientUuid; | |
703 MainDicomTagsToJson(result, patientUuid); | |
704 | |
705 Json::Value studies(Json::arrayValue); | |
706 SQLite::Statement s2(db_, SQLITE_FROM_HERE, "SELECT uuid FROM Studies WHERE parentPatient=?"); | |
707 s2.BindString(0, patientUuid); | |
708 while (s2.Step()) | |
709 { | |
710 studies.append(s2.ColumnString(0)); | |
711 } | |
712 | |
713 result["Studies"] = studies; | |
714 return true; | |
715 } | |
716 | |
717 | |
718 bool ServerIndex::GetJsonFile(std::string& fileUuid, | |
719 const std::string& instanceUuid) | |
720 { | |
721 boost::mutex::scoped_lock scoped_lock(mutex_); | |
722 | |
723 SQLite::Statement s(db_, SQLITE_FROM_HERE, "SELECT jsonUuid FROM Instances WHERE uuid=?"); | |
724 s.BindString(0, instanceUuid); | |
725 if (s.Step()) | |
726 { | |
727 fileUuid = s.ColumnString(0); | |
728 return true; | |
729 } | |
730 else | |
731 { | |
732 return false; | |
733 } | |
734 } | |
735 | |
736 bool ServerIndex::GetDicomFile(std::string& fileUuid, | |
737 const std::string& instanceUuid) | |
738 { | |
739 boost::mutex::scoped_lock scoped_lock(mutex_); | |
740 | |
741 SQLite::Statement s(db_, SQLITE_FROM_HERE, "SELECT fileUuid FROM Instances WHERE uuid=?"); | |
742 s.BindString(0, instanceUuid); | |
743 if (s.Step()) | |
744 { | |
745 fileUuid = s.ColumnString(0); | |
746 return true; | |
747 } | |
748 else | |
749 { | |
750 return false; | |
751 } | |
752 } | |
753 | |
754 | |
755 void ServerIndex::GetAllUuids(Json::Value& target, | |
756 const std::string& tableName) | |
757 { | |
758 assert(target.type() == Json::arrayValue); | |
759 boost::mutex::scoped_lock scoped_lock(mutex_); | |
760 | |
761 std::string query = "SELECT uuid FROM " + tableName; | |
762 SQLite::Statement s(db_, query); | |
763 while (s.Step()) | |
764 { | |
765 target.append(s.ColumnString(0)); | |
766 } | |
767 } | |
768 | |
769 | |
770 bool ServerIndex::GetChanges(Json::Value& target, | |
771 int64_t since, | |
772 const std::string& filter, | |
773 unsigned int maxResults) | |
774 { | |
775 assert(target.type() == Json::objectValue); | |
776 boost::mutex::scoped_lock scoped_lock(mutex_); | |
777 | |
778 if (filter.size() != 0 && | |
779 filter != "instances" && | |
780 filter != "series" && | |
781 filter != "studies" && | |
782 filter != "patients") | |
783 { | |
784 return false; | |
785 } | |
786 | |
787 std::auto_ptr<SQLite::Statement> s; | |
788 if (filter.size() == 0) | |
789 { | |
790 s.reset(new SQLite::Statement(db_, SQLITE_FROM_HERE, "SELECT * FROM Changes WHERE seq>? " | |
791 "ORDER BY seq LIMIT ?")); | |
792 s->BindInt64(0, since); | |
793 s->BindInt(1, maxResults); | |
794 } | |
795 else | |
796 { | |
797 s.reset(new SQLite::Statement(db_, SQLITE_FROM_HERE, "SELECT * FROM Changes WHERE seq>? " | |
798 "AND basePath=? ORDER BY seq LIMIT ?")); | |
799 s->BindInt64(0, since); | |
800 s->BindString(1, filter); | |
801 s->BindInt(2, maxResults); | |
802 } | |
803 | |
804 int64_t lastSeq = 0; | |
805 Json::Value results(Json::arrayValue); | |
806 while (s->Step()) | |
807 { | |
808 int64_t seq = s->ColumnInt64(0); | |
809 std::string basePath = s->ColumnString(1); | |
810 std::string uuid = s->ColumnString(2); | |
811 | |
812 if (filter.size() == 0 || | |
813 filter == basePath) | |
814 { | |
815 Json::Value change(Json::objectValue); | |
816 change["Seq"] = static_cast<int>(seq); // TODO JsonCpp in 64bit | |
817 change["BasePath"] = basePath; | |
818 change["ID"] = uuid; | |
819 results.append(change); | |
820 } | |
821 | |
822 if (seq > lastSeq) | |
823 { | |
824 lastSeq = seq; | |
825 } | |
826 } | |
827 | |
828 target["Results"] = results; | |
829 target["LastSeq"] = static_cast<int>(lastSeq); // TODO JsonCpp in 64bit | |
830 | |
831 return true; | |
832 } | |
833 } |