comparison OrthancServer/ServerIndex.cpp @ 202:1650557bd81a

refactoring
author Sebastien Jodogne <s.jodogne@gmail.com>
date Tue, 27 Nov 2012 17:48:37 +0100
parents bee20e978835
children 7f4acf490179
comparison
equal deleted inserted replaced
201:bee20e978835 202:1650557bd81a
51 51
52 namespace Orthanc 52 namespace Orthanc
53 { 53 {
54 namespace Internals 54 namespace Internals
55 { 55 {
56 class ServerIndexListenerTodo : public IServerIndexListener 56 class ServerIndexListener : public IServerIndexListener
57 { 57 {
58 private: 58 private:
59 FileStorage& fileStorage_; 59 FileStorage& fileStorage_;
60 bool hasRemainingLevel_; 60 bool hasRemainingLevel_;
61 ResourceType remainingType_; 61 ResourceType remainingType_;
62 std::string remainingPublicId_; 62 std::string remainingPublicId_;
63 63
64 public: 64 public:
65 ServerIndexListenerTodo(FileStorage& fileStorage) : 65 ServerIndexListener(FileStorage& fileStorage) :
66 fileStorage_(fileStorage), 66 fileStorage_(fileStorage),
67 hasRemainingLevel_(false) 67 hasRemainingLevel_(false)
68 { 68 {
69 assert(ResourceType_Patient < ResourceType_Study && 69 assert(ResourceType_Patient < ResourceType_Study &&
70 ResourceType_Study < ResourceType_Series && 70 ResourceType_Study < ResourceType_Series &&
219 } 219 }
220 }; 220 };
221 } 221 }
222 222
223 223
224 void ServerIndex::StoreMainDicomTags(const std::string& uuid,
225 const DicomMap& map)
226 {
227 DicomArray flattened(map);
228 for (size_t i = 0; i < flattened.GetSize(); i++)
229 {
230 SQLite::Statement s(db_, SQLITE_FROM_HERE, "INSERT INTO MainDicomTags VALUES(?, ?, ?, ?)");
231 s.BindString(0, uuid);
232 s.BindInt(1, flattened.GetElement(i).GetTag().GetGroup());
233 s.BindInt(2, flattened.GetElement(i).GetTag().GetElement());
234 s.BindString(3, flattened.GetElement(i).GetValue().AsString());
235 s.Run();
236 }
237 }
238
239 bool ServerIndex::HasInstance(DicomInstanceHasher& hasher)
240 {
241 SQLite::Statement s(db_, SQLITE_FROM_HERE, "SELECT uuid FROM Instances WHERE dicomInstance=?");
242 s.BindString(0, hasher.GetInstanceUid());
243 return s.Step();
244 }
245
246
247 void ServerIndex::RecordChange(const std::string& resourceType,
248 const std::string& uuid)
249 {
250 SQLite::Statement s(db_, SQLITE_FROM_HERE, "INSERT INTO Changes VALUES(NULL, ?, ?)");
251 s.BindString(0, resourceType);
252 s.BindString(1, uuid);
253 s.Run();
254 }
255
256
257 void ServerIndex::CreateInstance(DicomInstanceHasher& hasher,
258 const DicomMap& dicomSummary,
259 const std::string& fileUuid,
260 uint64_t fileSize,
261 const std::string& jsonUuid,
262 const std::string& remoteAet)
263 {
264 SQLite::Statement s2(db_, SQLITE_FROM_HERE, "INSERT INTO Resources VALUES(?, ?)");
265 s2.BindString(0, hasher.HashInstance());
266 s2.BindInt(1, ResourceType_Instance);
267 s2.Run();
268
269 SQLite::Statement s(db_, SQLITE_FROM_HERE, "INSERT INTO Instances VALUES(?, ?, ?, ?, ?, ?, ?, ?)");
270 s.BindString(0, hasher.HashInstance());
271 s.BindString(1, hasher.HashSeries());
272 s.BindString(2, hasher.GetInstanceUid());
273 s.BindString(3, fileUuid);
274 s.BindInt64(4, fileSize);
275 s.BindString(5, jsonUuid);
276 s.BindString(6, remoteAet);
277
278 const DicomValue* indexInSeries;
279 if ((indexInSeries = dicomSummary.TestAndGetValue(DICOM_TAG_INSTANCE_NUMBER)) != NULL ||
280 (indexInSeries = dicomSummary.TestAndGetValue(DICOM_TAG_IMAGE_INDEX)) != NULL)
281 {
282 s.BindInt(7, boost::lexical_cast<unsigned int>(indexInSeries->AsString()));
283 }
284 else
285 {
286 s.BindNull(7);
287 }
288
289 s.Run();
290
291 RecordChange("instances", hasher.HashInstance());
292
293 DicomMap dicom;
294 dicomSummary.ExtractInstanceInformation(dicom);
295 StoreMainDicomTags(hasher.HashInstance(), dicom);
296 }
297
298 void ServerIndex::RemoveInstance(const std::string& uuid)
299 {
300 SQLite::Statement s(db_, SQLITE_FROM_HERE, "DELETE FROM Instances WHERE uuid=?");
301 s.BindString(0, uuid);
302 s.Run();
303 }
304
305 bool ServerIndex::HasSeries(DicomInstanceHasher& hasher)
306 {
307 SQLite::Statement s(db_, SQLITE_FROM_HERE, "SELECT uuid FROM Series WHERE dicomSeries=?");
308 s.BindString(0, hasher.GetSeriesUid());
309 return s.Step();
310 }
311
312 void ServerIndex::CreateSeries(DicomInstanceHasher& hasher,
313 const DicomMap& dicomSummary)
314 {
315 SQLite::Statement s2(db_, SQLITE_FROM_HERE, "INSERT INTO Resources VALUES(?, ?)");
316 s2.BindString(0, hasher.HashSeries());
317 s2.BindInt(1, ResourceType_Series);
318 s2.Run();
319
320 SQLite::Statement s(db_, SQLITE_FROM_HERE, "INSERT INTO Series VALUES(?, ?, ?, ?)");
321 s.BindString(0, hasher.HashSeries());
322 s.BindString(1, hasher.HashStudy());
323 s.BindString(2, hasher.GetSeriesUid());
324
325 const DicomValue* expectedNumberOfInstances;
326 if (//(expectedNumberOfInstances = dicomSummary.TestAndGetValue(DICOM_TAG_NUMBER_OF_FRAMES)) != NULL ||
327 (expectedNumberOfInstances = dicomSummary.TestAndGetValue(DICOM_TAG_NUMBER_OF_SLICES)) != NULL ||
328 //(expectedNumberOfInstances = dicomSummary.TestAndGetValue(DICOM_TAG_CARDIAC_NUMBER_OF_IMAGES)) != NULL ||
329 (expectedNumberOfInstances = dicomSummary.TestAndGetValue(DICOM_TAG_IMAGES_IN_ACQUISITION)) != NULL)
330 {
331 s.BindInt(3, boost::lexical_cast<unsigned int>(expectedNumberOfInstances->AsString()));
332 }
333 else
334 {
335 s.BindNull(3);
336 }
337
338 s.Run();
339
340 RecordChange("series", hasher.HashSeries());
341
342 DicomMap dicom;
343 dicomSummary.ExtractSeriesInformation(dicom);
344 StoreMainDicomTags(hasher.HashSeries(), dicom);
345 }
346
347 bool ServerIndex::HasStudy(DicomInstanceHasher& hasher)
348 {
349 SQLite::Statement s(db_, SQLITE_FROM_HERE, "SELECT uuid FROM Studies WHERE dicomStudy=?");
350 s.BindString(0, hasher.GetStudyUid());
351 return s.Step();
352 }
353
354 void ServerIndex::CreateStudy(DicomInstanceHasher& hasher,
355 const DicomMap& dicomSummary)
356 {
357 SQLite::Statement s2(db_, SQLITE_FROM_HERE, "INSERT INTO Resources VALUES(?, ?)");
358 s2.BindString(0, hasher.HashStudy());
359 s2.BindInt(1, ResourceType_Study);
360 s2.Run();
361
362 SQLite::Statement s(db_, SQLITE_FROM_HERE, "INSERT INTO Studies VALUES(?, ?, ?)");
363 s.BindString(0, hasher.HashStudy());
364 s.BindString(1, hasher.HashPatient());
365 s.BindString(2, hasher.GetStudyUid());
366 s.Run();
367
368 RecordChange("studies", hasher.HashStudy());
369
370 DicomMap dicom;
371 dicomSummary.ExtractStudyInformation(dicom);
372 StoreMainDicomTags(hasher.HashStudy(), dicom);
373 }
374
375
376
377 bool ServerIndex::HasPatient(DicomInstanceHasher& hasher)
378 {
379 SQLite::Statement s(db_, SQLITE_FROM_HERE, "SELECT uuid FROM Patients WHERE dicomPatientId=?");
380 s.BindString(0,hasher.GetPatientId());
381 return s.Step();
382 }
383
384 void ServerIndex::CreatePatient(DicomInstanceHasher& hasher,
385 const DicomMap& dicomSummary)
386 {
387 std::string dicomPatientId = dicomSummary.GetValue(DICOM_TAG_PATIENT_ID).AsString();
388
389 SQLite::Statement s2(db_, SQLITE_FROM_HERE, "INSERT INTO Resources VALUES(?, ?)");
390 s2.BindString(0, hasher.HashPatient());
391 s2.BindInt(1, ResourceType_Patient);
392 s2.Run();
393
394 SQLite::Statement s(db_, SQLITE_FROM_HERE, "INSERT INTO Patients VALUES(?, ?)");
395 s.BindString(0, hasher.HashPatient());
396 s.BindString(1, dicomPatientId);
397 s.Run();
398
399 RecordChange("patients", hasher.HashPatient());
400
401 DicomMap dicom;
402 dicomSummary.ExtractPatientInformation(dicom);
403 StoreMainDicomTags(hasher.HashPatient(), dicom);
404 }
405
406
407 bool ServerIndex::DeleteInternal(Json::Value& target, 224 bool ServerIndex::DeleteInternal(Json::Value& target,
408 const std::string& uuid, 225 const std::string& uuid,
409 const std::string& tableName) 226 ResourceType expectedType)
410 { 227 {
411 boost::mutex::scoped_lock scoped_lock(mutex_); 228 boost::mutex::scoped_lock scoped_lock(mutex_);
412 229
413 230 listener_->Reset();
414 { 231
415 listener2_->Reset(); 232 std::auto_ptr<SQLite::Transaction> t(db_->StartTransaction());
416 233 t->Begin();
417 std::auto_ptr<SQLite::Transaction> t(db2_->StartTransaction()); 234
418 t->Begin(); 235 int64_t id;
419 236 ResourceType type;
420 int64_t id; 237 if (!db_->LookupResource(uuid, id, type) ||
421 ResourceType type; 238 expectedType != type)
422 if (!db2_->LookupResource(uuid, id, type)) 239 {
423 { 240 return false;
424 return false; 241 }
425 }
426 242
427 db2_->DeleteResource(id); 243 db_->DeleteResource(id);
428 244
429 if (listener2_->HasRemainingLevel()) 245 if (listener_->HasRemainingLevel())
430 { 246 {
431 ResourceType type = listener2_->GetRemainingType(); 247 ResourceType type = listener_->GetRemainingType();
432 const std::string& uuid = listener2_->GetRemainingPublicId(); 248 const std::string& uuid = listener_->GetRemainingPublicId();
433
434 target["RemainingAncestor"] = Json::Value(Json::objectValue);
435 target["RemainingAncestor"]["Path"] = std::string(GetBasePath(type)) + "/" + uuid;
436 target["RemainingAncestor"]["Type"] = ToString(type);
437 target["RemainingAncestor"]["ID"] = uuid;
438 }
439 else
440 {
441 target["RemainingAncestor"] = Json::nullValue;
442 }
443
444 std::cout << target << std::endl;
445
446 t->Commit();
447
448 return true;
449 }
450
451
452
453 deletedLevels_->Clear();
454
455 SQLite::Statement s(db_, "DELETE FROM " + tableName + " WHERE uuid=?");
456 s.BindString(0, uuid);
457
458 if (!s.Run())
459 {
460 return false;
461 }
462
463 if (db_.GetLastChangeCount() == 0)
464 {
465 // Nothing was deleted, inexistent UUID
466 return false;
467 }
468
469 if (deletedLevels_->HasRemainingLevel())
470 {
471 std::string type(deletedLevels_->GetRemainingLevelType());
472 const std::string& uuid = deletedLevels_->GetRemainingLevelUuid();
473 249
474 target["RemainingAncestor"] = Json::Value(Json::objectValue); 250 target["RemainingAncestor"] = Json::Value(Json::objectValue);
475 target["RemainingAncestor"]["Path"] = "/" + type + "/" + uuid; 251 target["RemainingAncestor"]["Path"] = std::string(GetBasePath(type)) + "/" + uuid;
476 target["RemainingAncestor"]["Type"] = type; 252 target["RemainingAncestor"]["Type"] = ToString(type);
477 target["RemainingAncestor"]["ID"] = uuid; 253 target["RemainingAncestor"]["ID"] = uuid;
478 } 254 }
479 else 255 else
480 { 256 {
481 target["RemainingAncestor"] = Json::nullValue; 257 target["RemainingAncestor"] = Json::nullValue;
482 } 258 }
259
260 t->Commit();
483 261
484 return true; 262 return true;
485 } 263 }
486 264
487 265
488 ServerIndex::ServerIndex(FileStorage& fileStorage, 266 ServerIndex::ServerIndex(FileStorage& fileStorage,
489 const std::string& dbPath) 267 const std::string& dbPath)
490 { 268 {
491 listener2_.reset(new Internals::ServerIndexListenerTodo(fileStorage)); 269 listener_.reset(new Internals::ServerIndexListener(fileStorage));
492 270
493 if (dbPath == ":memory:") 271 if (dbPath == ":memory:")
494 { 272 {
495 db_.OpenInMemory(); 273 db_.reset(new DatabaseWrapper(*listener_));
496 db2_.reset(new DatabaseWrapper(*listener2_));
497 } 274 }
498 else 275 else
499 { 276 {
500 boost::filesystem::path p = dbPath; 277 boost::filesystem::path p = dbPath;
501 278
505 } 282 }
506 catch (boost::filesystem::filesystem_error) 283 catch (boost::filesystem::filesystem_error)
507 { 284 {
508 } 285 }
509 286
510 db2_.reset(new DatabaseWrapper(p.string() + "/index2", *listener2_)); 287 db_.reset(new DatabaseWrapper(p.string() + "/index", *listener_));
511 288 }
512 p /= "index"; 289 }
513 db_.Open(p.string()); 290
514 } 291
515 292 StoreStatus ServerIndex::Store(const DicomMap& dicomSummary,
516 db_.Register(new Internals::DeleteFromFileStorageFunction(fileStorage)); 293 const std::string& fileUuid,
517 deletedLevels_ = (Internals::SignalDeletedLevelFunction*) 294 uint64_t uncompressedFileSize,
518 db_.Register(new Internals::SignalDeletedLevelFunction); 295 const std::string& jsonUuid,
519 296 const std::string& remoteAet)
520 if (!db_.DoesTableExist("GlobalProperties"))
521 {
522 LOG(INFO) << "Creating the database";
523 std::string query;
524 EmbeddedResources::GetFileResource(query, EmbeddedResources::PREPARE_DATABASE);
525 db_.Execute(query);
526 }
527 }
528
529
530 StoreStatus ServerIndex::Store2(const DicomMap& dicomSummary,
531 const std::string& fileUuid,
532 uint64_t uncompressedFileSize,
533 const std::string& jsonUuid,
534 const std::string& remoteAet)
535 { 297 {
536 boost::mutex::scoped_lock scoped_lock(mutex_); 298 boost::mutex::scoped_lock scoped_lock(mutex_);
537 299
538 DicomInstanceHasher hasher(dicomSummary); 300 DicomInstanceHasher hasher(dicomSummary);
539 301
540 try 302 try
541 { 303 {
542 std::auto_ptr<SQLite::Transaction> t(db2_->StartTransaction()); 304 std::auto_ptr<SQLite::Transaction> t(db_->StartTransaction());
543 t->Begin(); 305 t->Begin();
544 306
545 int64_t patient, study, series, instance; 307 int64_t patient, study, series, instance;
546 ResourceType type; 308 ResourceType type;
547 bool isNewSeries = false; 309 bool isNewSeries = false;
548 310
549 // Do nothing if the instance already exists 311 // Do nothing if the instance already exists
550 if (db2_->LookupResource(hasher.HashInstance(), patient, type)) 312 if (db_->LookupResource(hasher.HashInstance(), patient, type))
551 { 313 {
552 assert(type == ResourceType_Instance); 314 assert(type == ResourceType_Instance);
553 return StoreStatus_AlreadyStored; 315 return StoreStatus_AlreadyStored;
554 } 316 }
555 317
556 // Create the instance 318 // Create the instance
557 instance = db2_->CreateResource(hasher.HashInstance(), ResourceType_Instance); 319 instance = db_->CreateResource(hasher.HashInstance(), ResourceType_Instance);
558 320
559 DicomMap dicom; 321 DicomMap dicom;
560 dicomSummary.ExtractInstanceInformation(dicom); 322 dicomSummary.ExtractInstanceInformation(dicom);
561 db2_->SetMainDicomTags(instance, dicom); 323 db_->SetMainDicomTags(instance, dicom);
562 324
563 // Create the patient/study/series/instance hierarchy 325 // Create the patient/study/series/instance hierarchy
564 if (!db2_->LookupResource(hasher.HashSeries(), series, type)) 326 if (!db_->LookupResource(hasher.HashSeries(), series, type))
565 { 327 {
566 // This is a new series 328 // This is a new series
567 isNewSeries = true; 329 isNewSeries = true;
568 series = db2_->CreateResource(hasher.HashSeries(), ResourceType_Series); 330 series = db_->CreateResource(hasher.HashSeries(), ResourceType_Series);
569 dicomSummary.ExtractSeriesInformation(dicom); 331 dicomSummary.ExtractSeriesInformation(dicom);
570 db2_->SetMainDicomTags(series, dicom); 332 db_->SetMainDicomTags(series, dicom);
571 db2_->AttachChild(series, instance); 333 db_->AttachChild(series, instance);
572 334
573 if (!db2_->LookupResource(hasher.HashStudy(), study, type)) 335 if (!db_->LookupResource(hasher.HashStudy(), study, type))
574 { 336 {
575 // This is a new study 337 // This is a new study
576 study = db2_->CreateResource(hasher.HashStudy(), ResourceType_Study); 338 study = db_->CreateResource(hasher.HashStudy(), ResourceType_Study);
577 dicomSummary.ExtractStudyInformation(dicom); 339 dicomSummary.ExtractStudyInformation(dicom);
578 db2_->SetMainDicomTags(study, dicom); 340 db_->SetMainDicomTags(study, dicom);
579 db2_->AttachChild(study, series); 341 db_->AttachChild(study, series);
580 342
581 if (!db2_->LookupResource(hasher.HashPatient(), patient, type)) 343 if (!db_->LookupResource(hasher.HashPatient(), patient, type))
582 { 344 {
583 // This is a new patient 345 // This is a new patient
584 patient = db2_->CreateResource(hasher.HashPatient(), ResourceType_Patient); 346 patient = db_->CreateResource(hasher.HashPatient(), ResourceType_Patient);
585 dicomSummary.ExtractPatientInformation(dicom); 347 dicomSummary.ExtractPatientInformation(dicom);
586 db2_->SetMainDicomTags(patient, dicom); 348 db_->SetMainDicomTags(patient, dicom);
587 db2_->AttachChild(patient, study); 349 db_->AttachChild(patient, study);
588 } 350 }
589 else 351 else
590 { 352 {
591 assert(type == ResourceType_Patient); 353 assert(type == ResourceType_Patient);
592 db2_->AttachChild(patient, study); 354 db_->AttachChild(patient, study);
593 } 355 }
594 } 356 }
595 else 357 else
596 { 358 {
597 assert(type == ResourceType_Study); 359 assert(type == ResourceType_Study);
598 db2_->AttachChild(study, series); 360 db_->AttachChild(study, series);
599 } 361 }
600 } 362 }
601 else 363 else
602 { 364 {
603 assert(type == ResourceType_Series); 365 assert(type == ResourceType_Series);
604 db2_->AttachChild(series, instance); 366 db_->AttachChild(series, instance);
605 } 367 }
606 368
607 // Attach the files to the newly created instance 369 // Attach the files to the newly created instance
608 db2_->AttachFile(instance, AttachedFileType_Dicom, fileUuid, uncompressedFileSize); 370 db_->AttachFile(instance, AttachedFileType_Dicom, fileUuid, uncompressedFileSize);
609 db2_->AttachFile(instance, AttachedFileType_Json, jsonUuid, 0); // TODO "0" 371 db_->AttachFile(instance, AttachedFileType_Json, jsonUuid, 0); // TODO "0"
610 372
611 // Attach the metadata 373 // Attach the metadata
612 db2_->SetMetadata(instance, MetadataType_Instance_ReceptionDate, Toolbox::GetNowIsoString()); 374 db_->SetMetadata(instance, MetadataType_Instance_ReceptionDate, Toolbox::GetNowIsoString());
613 db2_->SetMetadata(instance, MetadataType_Instance_RemoteAet, remoteAet); 375 db_->SetMetadata(instance, MetadataType_Instance_RemoteAet, remoteAet);
614 376
615 const DicomValue* value; 377 const DicomValue* value;
616 if ((value = dicomSummary.TestAndGetValue(DICOM_TAG_INSTANCE_NUMBER)) != NULL || 378 if ((value = dicomSummary.TestAndGetValue(DICOM_TAG_INSTANCE_NUMBER)) != NULL ||
617 (value = dicomSummary.TestAndGetValue(DICOM_TAG_IMAGE_INDEX)) != NULL) 379 (value = dicomSummary.TestAndGetValue(DICOM_TAG_IMAGE_INDEX)) != NULL)
618 { 380 {
619 db2_->SetMetadata(instance, MetadataType_Instance_IndexInSeries, value->AsString()); 381 db_->SetMetadata(instance, MetadataType_Instance_IndexInSeries, value->AsString());
620 } 382 }
621 383
622 if (isNewSeries) 384 if (isNewSeries)
623 { 385 {
624 if ((value = dicomSummary.TestAndGetValue(DICOM_TAG_NUMBER_OF_SLICES)) != NULL || 386 if ((value = dicomSummary.TestAndGetValue(DICOM_TAG_NUMBER_OF_SLICES)) != NULL ||
625 (value = dicomSummary.TestAndGetValue(DICOM_TAG_IMAGES_IN_ACQUISITION)) != NULL || 387 (value = dicomSummary.TestAndGetValue(DICOM_TAG_IMAGES_IN_ACQUISITION)) != NULL ||
626 (value = dicomSummary.TestAndGetValue(DICOM_TAG_CARDIAC_NUMBER_OF_IMAGES)) != NULL) 388 (value = dicomSummary.TestAndGetValue(DICOM_TAG_CARDIAC_NUMBER_OF_IMAGES)) != NULL)
627 { 389 {
628 db2_->SetMetadata(series, MetadataType_Series_ExpectedNumberOfInstances, value->AsString()); 390 db_->SetMetadata(series, MetadataType_Series_ExpectedNumberOfInstances, value->AsString());
629 } 391 }
630 } 392 }
631 393
632 t->Commit(); 394 t->Commit();
395
396 return StoreStatus_Success;
633 } 397 }
634 catch (OrthancException& e) 398 catch (OrthancException& e)
635 { 399 {
636 LOG(ERROR) << "EXCEPTION2 [" << e.What() << "]" << " " << db_.GetErrorMessage(); 400 LOG(ERROR) << "EXCEPTION2 [" << e.What() << "]" << " " << db_->GetErrorMessage();
637 }
638
639 return StoreStatus_Failure;
640 }
641
642
643 StoreStatus ServerIndex::Store(const DicomMap& dicomSummary,
644 const std::string& fileUuid,
645 uint64_t uncompressedFileSize,
646 const std::string& jsonUuid,
647 const std::string& remoteAet)
648 {
649 Store2(dicomSummary, fileUuid, uncompressedFileSize, jsonUuid, remoteAet);
650
651 boost::mutex::scoped_lock scoped_lock(mutex_);
652
653 DicomInstanceHasher hasher(dicomSummary);
654
655 try
656 {
657 SQLite::Transaction t(db_);
658 t.Begin();
659
660 if (HasInstance(hasher))
661 {
662 return StoreStatus_AlreadyStored;
663 // TODO: Check consistency?
664 }
665
666 if (HasPatient(hasher))
667 {
668 // TODO: Check consistency?
669 }
670 else
671 {
672 CreatePatient(hasher, dicomSummary);
673 }
674
675 if (HasStudy(hasher))
676 {
677 // TODO: Check consistency?
678 }
679 else
680 {
681 CreateStudy(hasher, dicomSummary);
682 }
683
684 if (HasSeries(hasher))
685 {
686 // TODO: Check consistency?
687 }
688 else
689 {
690 CreateSeries(hasher, dicomSummary);
691 }
692
693 CreateInstance(hasher, dicomSummary, fileUuid,
694 uncompressedFileSize, jsonUuid, remoteAet);
695
696 t.Commit();
697 return StoreStatus_Success;
698 //t.Rollback();
699 }
700 catch (OrthancException& e)
701 {
702 LOG(ERROR) << "EXCEPTION [" << e.What() << "]" << " " << db_.GetErrorMessage();
703 } 401 }
704 402
705 return StoreStatus_Failure; 403 return StoreStatus_Failure;
706 } 404 }
707 405
742 } 440 }
743 441
744 uint64_t ServerIndex::GetTotalCompressedSize() 442 uint64_t ServerIndex::GetTotalCompressedSize()
745 { 443 {
746 boost::mutex::scoped_lock scoped_lock(mutex_); 444 boost::mutex::scoped_lock scoped_lock(mutex_);
747 return db2_->GetTotalCompressedSize(); 445 return db_->GetTotalCompressedSize();
748 } 446 }
749 447
750 uint64_t ServerIndex::GetTotalUncompressedSize() 448 uint64_t ServerIndex::GetTotalUncompressedSize()
751 { 449 {
752 boost::mutex::scoped_lock scoped_lock(mutex_); 450 boost::mutex::scoped_lock scoped_lock(mutex_);
753 return db2_->GetTotalUncompressedSize(); 451 return db_->GetTotalUncompressedSize();
754 } 452 }
755 453
756 454
757 SeriesStatus ServerIndex::GetSeriesStatus(int id) 455 SeriesStatus ServerIndex::GetSeriesStatus(int id)
758 { 456 {
759 // Get the expected number of instances in this series (from the metadata) 457 // Get the expected number of instances in this series (from the metadata)
760 std::string s = db2_->GetMetadata(id, MetadataType_Series_ExpectedNumberOfInstances); 458 std::string s = db_->GetMetadata(id, MetadataType_Series_ExpectedNumberOfInstances);
761 459
762 size_t expected; 460 size_t expected;
763 try 461 try
764 { 462 {
765 expected = boost::lexical_cast<size_t>(s); 463 expected = boost::lexical_cast<size_t>(s);
773 return SeriesStatus_Unknown; 471 return SeriesStatus_Unknown;
774 } 472 }
775 473
776 // Loop over the instances of this series 474 // Loop over the instances of this series
777 std::list<int64_t> children; 475 std::list<int64_t> children;
778 db2_->GetChildrenInternalId(children, id); 476 db_->GetChildrenInternalId(children, id);
779 477
780 std::set<size_t> instances; 478 std::set<size_t> instances;
781 for (std::list<int64_t>::const_iterator 479 for (std::list<int64_t>::const_iterator
782 it = children.begin(); it != children.end(); it++) 480 it = children.begin(); it != children.end(); it++)
783 { 481 {
784 // Get the index of this instance in the series 482 // Get the index of this instance in the series
785 s = db2_->GetMetadata(*it, MetadataType_Instance_IndexInSeries); 483 s = db_->GetMetadata(*it, MetadataType_Instance_IndexInSeries);
786 size_t index; 484 size_t index;
787 try 485 try
788 { 486 {
789 index = boost::lexical_cast<size_t>(s); 487 index = boost::lexical_cast<size_t>(s);
790 } 488 }
818 } 516 }
819 } 517 }
820 518
821 519
822 520
823 void ServerIndex::MainDicomTagsToJson2(Json::Value& target, 521 void ServerIndex::MainDicomTagsToJson(Json::Value& target,
824 int64_t resourceId) 522 int64_t resourceId)
825 { 523 {
826 DicomMap tags; 524 DicomMap tags;
827 db2_->GetMainDicomTags(tags, resourceId); 525 db_->GetMainDicomTags(tags, resourceId);
828 target["MainDicomTags"] = Json::objectValue; 526 target["MainDicomTags"] = Json::objectValue;
829 FromDcmtkBridge::ToJson(target["MainDicomTags"], tags); 527 FromDcmtkBridge::ToJson(target["MainDicomTags"], tags);
830 } 528 }
831 529
832 bool ServerIndex::LookupResource(Json::Value& result, 530 bool ServerIndex::LookupResource(Json::Value& result,
838 boost::mutex::scoped_lock scoped_lock(mutex_); 536 boost::mutex::scoped_lock scoped_lock(mutex_);
839 537
840 // Lookup for the requested resource 538 // Lookup for the requested resource
841 int64_t id; 539 int64_t id;
842 ResourceType type; 540 ResourceType type;
843 if (!db2_->LookupResource(publicId, id, type) || 541 if (!db_->LookupResource(publicId, id, type) ||
844 type != expectedType) 542 type != expectedType)
845 { 543 {
846 return false; 544 return false;
847 } 545 }
848 546
849 // Find the parent resource (if it exists) 547 // Find the parent resource (if it exists)
850 if (type != ResourceType_Patient) 548 if (type != ResourceType_Patient)
851 { 549 {
852 int64_t parentId; 550 int64_t parentId;
853 if (!db2_->LookupParent(parentId, id)) 551 if (!db_->LookupParent(parentId, id))
854 { 552 {
855 throw OrthancException(ErrorCode_InternalError); 553 throw OrthancException(ErrorCode_InternalError);
856 } 554 }
857 555
858 std::string parent = db2_->GetPublicId(parentId); 556 std::string parent = db_->GetPublicId(parentId);
859 557
860 switch (type) 558 switch (type)
861 { 559 {
862 case ResourceType_Study: 560 case ResourceType_Study:
863 result["ParentPatient"] = parent; 561 result["ParentPatient"] = parent;
876 } 574 }
877 } 575 }
878 576
879 // List the children resources 577 // List the children resources
880 std::list<std::string> children; 578 std::list<std::string> children;
881 db2_->GetChildrenPublicId(children, id); 579 db_->GetChildrenPublicId(children, id);
882 580
883 if (type != ResourceType_Instance) 581 if (type != ResourceType_Instance)
884 { 582 {
885 Json::Value c = Json::arrayValue; 583 Json::Value c = Json::arrayValue;
886 584
924 { 622 {
925 result["Type"] = "Series"; 623 result["Type"] = "Series";
926 result["Status"] = ToString(GetSeriesStatus(id)); 624 result["Status"] = ToString(GetSeriesStatus(id));
927 625
928 int i; 626 int i;
929 if (db2_->GetMetadataAsInteger(i, id, MetadataType_Series_ExpectedNumberOfInstances)) 627 if (db_->GetMetadataAsInteger(i, id, MetadataType_Series_ExpectedNumberOfInstances))
930 result["ExpectedNumberOfInstances"] = i; 628 result["ExpectedNumberOfInstances"] = i;
931 else 629 else
932 result["ExpectedNumberOfInstances"] = Json::nullValue; 630 result["ExpectedNumberOfInstances"] = Json::nullValue;
933 631
934 break; 632 break;
935 } 633 }
936 634
938 { 636 {
939 result["Type"] = "Instance"; 637 result["Type"] = "Instance";
940 638
941 std::string fileUuid; 639 std::string fileUuid;
942 uint64_t uncompressedSize; 640 uint64_t uncompressedSize;
943 if (!db2_->LookupFile(id, AttachedFileType_Dicom, fileUuid, uncompressedSize)) 641 if (!db_->LookupFile(id, AttachedFileType_Dicom, fileUuid, uncompressedSize))
944 { 642 {
945 throw OrthancException(ErrorCode_InternalError); 643 throw OrthancException(ErrorCode_InternalError);
946 } 644 }
947 645
948 result["FileSize"] = static_cast<unsigned int>(uncompressedSize); 646 result["FileSize"] = static_cast<unsigned int>(uncompressedSize);
949 result["FileUuid"] = fileUuid; 647 result["FileUuid"] = fileUuid;
950 648
951 int i; 649 int i;
952 if (db2_->GetMetadataAsInteger(i, id, MetadataType_Instance_IndexInSeries)) 650 if (db_->GetMetadataAsInteger(i, id, MetadataType_Instance_IndexInSeries))
953 result["IndexInSeries"] = i; 651 result["IndexInSeries"] = i;
954 else 652 else
955 result["IndexInSeries"] = Json::nullValue; 653 result["IndexInSeries"] = Json::nullValue;
956 654
957 break; 655 break;
961 throw OrthancException(ErrorCode_InternalError); 659 throw OrthancException(ErrorCode_InternalError);
962 } 660 }
963 661
964 // Record the remaining information 662 // Record the remaining information
965 result["ID"] = publicId; 663 result["ID"] = publicId;
966 MainDicomTagsToJson2(result, id); 664 MainDicomTagsToJson(result, id);
967 665
968 return true; 666 return true;
969 } 667 }
970 668
971 669
976 { 674 {
977 boost::mutex::scoped_lock scoped_lock(mutex_); 675 boost::mutex::scoped_lock scoped_lock(mutex_);
978 676
979 int64_t id; 677 int64_t id;
980 ResourceType type; 678 ResourceType type;
981 if (!db2_->LookupResource(instanceUuid, id, type) || 679 if (!db_->LookupResource(instanceUuid, id, type) ||
982 type != ResourceType_Instance) 680 type != ResourceType_Instance)
983 { 681 {
984 throw OrthancException(ErrorCode_InternalError); 682 throw OrthancException(ErrorCode_InternalError);
985 } 683 }
986 684
987 uint64_t compressedSize, uncompressedSize; 685 uint64_t compressedSize, uncompressedSize;
988 686
989 return db2_->LookupFile(id, contentType, fileUuid, compressedSize, uncompressedSize, compressionType); 687 return db_->LookupFile(id, contentType, fileUuid, compressedSize, uncompressedSize, compressionType);
990 } 688 }
991 689
992 690
993 691
994 void ServerIndex::GetAllUuids(Json::Value& target, 692 void ServerIndex::GetAllUuids(Json::Value& target,
995 ResourceType resourceType) 693 ResourceType resourceType)
996 { 694 {
997 boost::mutex::scoped_lock scoped_lock(mutex_); 695 boost::mutex::scoped_lock scoped_lock(mutex_);
998 db2_->GetAllPublicIds(target, resourceType); 696 db_->GetAllPublicIds(target, resourceType);
999 } 697 }
1000 698
1001 699
1002 bool ServerIndex::GetChanges(Json::Value& target, 700 bool ServerIndex::GetChanges(Json::Value& target,
1003 int64_t since, 701 int64_t since,
1004 const std::string& filter, 702 const std::string& filter,
1005 unsigned int maxResults) 703 unsigned int maxResults)
1006 { 704 {
1007 assert(target.type() == Json::objectValue);
1008 boost::mutex::scoped_lock scoped_lock(mutex_); 705 boost::mutex::scoped_lock scoped_lock(mutex_);
1009 706 return false;
1010 if (filter.size() != 0 && 707 // TODO !!!!
1011 filter != "instances" && 708
1012 filter != "series" && 709 /*assert(target.type() == Json::objectValue);
1013 filter != "studies" && 710 boost::mutex::scoped_lock scoped_lock(mutex_);
1014 filter != "patients") 711
1015 { 712 if (filter.size() != 0 &&
713 filter != "instances" &&
714 filter != "series" &&
715 filter != "studies" &&
716 filter != "patients")
717 {
1016 return false; 718 return false;
1017 } 719 }
1018 720
1019 std::auto_ptr<SQLite::Statement> s; 721 std::auto_ptr<SQLite::Statement> s;
1020 if (filter.size() == 0) 722 if (filter.size() == 0)
1021 { 723 {
1022 s.reset(new SQLite::Statement(db_, SQLITE_FROM_HERE, "SELECT * FROM Changes WHERE seq>? " 724 s.reset(new SQLite::Statement(db_, SQLITE_FROM_HERE, "SELECT * FROM Changes WHERE seq>? "
1023 "ORDER BY seq LIMIT ?")); 725 "ORDER BY seq LIMIT ?"));
1024 s->BindInt64(0, since); 726 s->BindInt64(0, since);
1025 s->BindInt(1, maxResults); 727 s->BindInt(1, maxResults);
1026 } 728 }
1027 else 729 else
1028 { 730 {
1029 s.reset(new SQLite::Statement(db_, SQLITE_FROM_HERE, "SELECT * FROM Changes WHERE seq>? " 731 s.reset(new SQLite::Statement(db_, SQLITE_FROM_HERE, "SELECT * FROM Changes WHERE seq>? "
1030 "AND basePath=? ORDER BY seq LIMIT ?")); 732 "AND basePath=? ORDER BY seq LIMIT ?"));
1031 s->BindInt64(0, since); 733 s->BindInt64(0, since);
1032 s->BindString(1, filter); 734 s->BindString(1, filter);
1033 s->BindInt(2, maxResults); 735 s->BindInt(2, maxResults);
1034 } 736 }
1035 737
1036 int64_t lastSeq = 0; 738 int64_t lastSeq = 0;
1037 Json::Value results(Json::arrayValue); 739 Json::Value results(Json::arrayValue);
1038 while (s->Step()) 740 while (s->Step())
1039 { 741 {
1040 int64_t seq = s->ColumnInt64(0); 742 int64_t seq = s->ColumnInt64(0);
1041 std::string basePath = s->ColumnString(1); 743 std::string basePath = s->ColumnString(1);
1042 std::string uuid = s->ColumnString(2); 744 std::string uuid = s->ColumnString(2);
1043 745
1044 if (filter.size() == 0 || 746 if (filter.size() == 0 ||
1045 filter == basePath) 747 filter == basePath)
1046 { 748 {
1047 Json::Value change(Json::objectValue); 749 Json::Value change(Json::objectValue);
1048 change["Seq"] = static_cast<int>(seq); // TODO JsonCpp in 64bit 750 change["Seq"] = static_cast<int>(seq); // TODO JsonCpp in 64bit
1049 change["BasePath"] = basePath; 751 change["BasePath"] = basePath;
1050 change["ID"] = uuid; 752 change["ID"] = uuid;
1051 results.append(change); 753 results.append(change);
1052 } 754 }
1053 755
1054 if (seq > lastSeq) 756 if (seq > lastSeq)
1055 { 757 {
1056 lastSeq = seq; 758 lastSeq = seq;
1057 } 759 }
1058 } 760 }
1059 761
1060 target["Results"] = results; 762 target["Results"] = results;
1061 target["LastSeq"] = static_cast<int>(lastSeq); // TODO JsonCpp in 64bit 763 target["LastSeq"] = static_cast<int>(lastSeq); // TODO JsonCpp in 64bit
1062 764
1063 return true; 765 return true;*/
1064 } 766 }
1065
1066 } 767 }