Mercurial > hg > orthanc
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 } |