Mercurial > hg > orthanc
comparison OrthancServer/OrthancRestApi/OrthancRestArchive.cpp @ 1774:784a6b92d2f1
start of refactoring the creation of archives
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Thu, 12 Nov 2015 17:49:04 +0100 |
parents | 76ed4cf74bb5 |
children | 1861e410a9d7 |
comparison
equal
deleted
inserted
replaced
1773:613df4362575 | 1774:784a6b92d2f1 |
---|---|
242 writer.CloseDirectory(); | 242 writer.CloseDirectory(); |
243 return true; | 243 return true; |
244 } | 244 } |
245 | 245 |
246 | 246 |
247 static bool IsZip64Required(ServerIndex& index, | 247 static bool IsZip64Required(uint64_t uncompressedSize, |
248 const std::string& id) | 248 unsigned int countInstances) |
249 { | 249 { |
250 static const uint64_t SAFETY_MARGIN = 64 * MEGA_BYTES; | |
251 | |
250 /** | 252 /** |
251 * Determine whether ZIP64 is required. Original ZIP format can | 253 * Determine whether ZIP64 is required. Original ZIP format can |
252 * store up to 2GB of data (some implementation supporting up to | 254 * store up to 2GB of data (some implementation supporting up to |
253 * 4GB of data), and up to 65535 files. | 255 * 4GB of data), and up to 65535 files. |
254 * https://en.wikipedia.org/wiki/Zip_(file_format)#ZIP64 | 256 * https://en.wikipedia.org/wiki/Zip_(file_format)#ZIP64 |
255 **/ | 257 **/ |
256 | 258 |
259 const bool isZip64 = (uncompressedSize >= 2 * GIGA_BYTES - SAFETY_MARGIN || | |
260 countInstances >= 65535); | |
261 | |
262 LOG(INFO) << "Creating a ZIP file with " << countInstances << " files of size " | |
263 << (uncompressedSize / MEGA_BYTES) << "MB using the " | |
264 << (isZip64 ? "ZIP64" : "ZIP32") << " file format"; | |
265 | |
266 return isZip64; | |
267 } | |
268 | |
269 | |
270 static bool IsZip64Required(ServerIndex& index, | |
271 const std::string& id) | |
272 { | |
257 uint64_t uncompressedSize; | 273 uint64_t uncompressedSize; |
258 uint64_t compressedSize; | 274 uint64_t compressedSize; |
259 unsigned int countStudies; | 275 unsigned int countStudies; |
260 unsigned int countSeries; | 276 unsigned int countSeries; |
261 unsigned int countInstances; | 277 unsigned int countInstances; |
278 | |
262 index.GetStatistics(compressedSize, uncompressedSize, | 279 index.GetStatistics(compressedSize, uncompressedSize, |
263 countStudies, countSeries, countInstances, id); | 280 countStudies, countSeries, countInstances, id); |
264 const bool isZip64 = (uncompressedSize >= 2 * GIGA_BYTES || | 281 |
265 countInstances >= 65535); | 282 return IsZip64Required(uncompressedSize, countInstances); |
266 | |
267 LOG(INFO) << "Creating a ZIP file with " << countInstances << " files of size " | |
268 << (uncompressedSize / MEGA_BYTES) << "MB using the " | |
269 << (isZip64 ? "ZIP64" : "ZIP32") << " file format"; | |
270 | |
271 return isZip64; | |
272 } | 283 } |
273 | 284 |
274 | 285 |
275 template <enum ResourceType resourceType> | 286 template <enum ResourceType resourceType> |
276 static void GetArchive(RestApiGetCall& call) | 287 static void GetArchive(RestApiGetCall& call) |
366 | 377 |
367 // The temporary file is automatically removed thanks to the RAII | 378 // The temporary file is automatically removed thanks to the RAII |
368 } | 379 } |
369 | 380 |
370 | 381 |
382 | |
383 | |
384 namespace | |
385 { | |
386 class Resource | |
387 { | |
388 private: | |
389 ResourceType level_; | |
390 std::string patient_; | |
391 std::string study_; | |
392 std::string series_; | |
393 std::string instance_; | |
394 | |
395 static void GoToParent(ServerIndex& index, | |
396 std::string& current) | |
397 { | |
398 std::string tmp; | |
399 | |
400 if (index.LookupParent(tmp, current)) | |
401 { | |
402 current = tmp; | |
403 } | |
404 else | |
405 { | |
406 throw OrthancException(ErrorCode_UnknownResource); | |
407 } | |
408 } | |
409 | |
410 | |
411 public: | |
412 Resource(ServerIndex& index, | |
413 const std::string& publicId) | |
414 { | |
415 if (!index.LookupResourceType(level_, publicId)) | |
416 { | |
417 throw OrthancException(ErrorCode_UnknownResource); | |
418 } | |
419 | |
420 std::string current = publicId;; | |
421 switch (level_) // Do not add "break" below! | |
422 { | |
423 case ResourceType_Instance: | |
424 instance_ = current; | |
425 GoToParent(index, current); | |
426 | |
427 case ResourceType_Series: | |
428 series_ = current; | |
429 GoToParent(index, current); | |
430 | |
431 case ResourceType_Study: | |
432 study_ = current; | |
433 GoToParent(index, current); | |
434 | |
435 case ResourceType_Patient: | |
436 patient_ = current; | |
437 break; | |
438 | |
439 default: | |
440 throw OrthancException(ErrorCode_InternalError); | |
441 } | |
442 } | |
443 | |
444 ResourceType GetLevel() const | |
445 { | |
446 return level_; | |
447 } | |
448 | |
449 const std::string& GetIdentifier(ResourceType level) const | |
450 { | |
451 // Some sanity check to ensure enumerations are not altered | |
452 assert(ResourceType_Patient < ResourceType_Study); | |
453 assert(ResourceType_Study < ResourceType_Series); | |
454 assert(ResourceType_Series < ResourceType_Instance); | |
455 | |
456 if (level > level_) | |
457 { | |
458 throw OrthancException(ErrorCode_InternalError); | |
459 } | |
460 | |
461 switch (level) | |
462 { | |
463 case ResourceType_Patient: | |
464 return patient_; | |
465 | |
466 case ResourceType_Study: | |
467 return study_; | |
468 | |
469 case ResourceType_Series: | |
470 return series_; | |
471 | |
472 case ResourceType_Instance: | |
473 return instance_; | |
474 | |
475 default: | |
476 throw OrthancException(ErrorCode_InternalError); | |
477 } | |
478 } | |
479 }; | |
480 | |
481 | |
482 class ArchiveIndex | |
483 { | |
484 private: | |
485 typedef std::map<std::string, ArchiveIndex*> Resources; | |
486 | |
487 ServerIndex& index_; | |
488 ResourceType level_; | |
489 Resources resources_; | |
490 | |
491 void AddResourceToExpand(const std::string& id) | |
492 { | |
493 resources_[id] = NULL; | |
494 } | |
495 | |
496 public: | |
497 ArchiveIndex(ServerIndex& index, | |
498 ResourceType level) : | |
499 index_(index), | |
500 level_(level) | |
501 { | |
502 } | |
503 | |
504 ~ArchiveIndex() | |
505 { | |
506 for (Resources::iterator it = resources_.begin(); | |
507 it != resources_.end(); ++it) | |
508 { | |
509 delete it->second; | |
510 } | |
511 } | |
512 | |
513 void Add(const Resource& resource) | |
514 { | |
515 const std::string& id = resource.GetIdentifier(level_); | |
516 Resources::iterator previous = resources_.find(id); | |
517 | |
518 if (resource.GetLevel() == level_) | |
519 { | |
520 // Mark this resource for further expansion | |
521 if (previous != resources_.end()) | |
522 { | |
523 delete previous->second; | |
524 } | |
525 | |
526 resources_[id] = NULL; | |
527 } | |
528 else if (previous == resources_.end()) | |
529 { | |
530 // This is the first time we meet this resource | |
531 std::auto_ptr<ArchiveIndex> child(new ArchiveIndex(index_, GetChildResourceType(level_))); | |
532 child->Add(resource); | |
533 resources_[id] = child.release(); | |
534 } | |
535 else if (previous->second != NULL) | |
536 { | |
537 previous->second->Add(resource); | |
538 } | |
539 else | |
540 { | |
541 // Nothing to do: This item is marked for further expansion | |
542 } | |
543 } | |
544 | |
545 | |
546 void Expand() | |
547 { | |
548 if (level_ == ResourceType_Instance) | |
549 { | |
550 return; | |
551 } | |
552 | |
553 for (Resources::iterator it = resources_.begin(); | |
554 it != resources_.end(); ++it) | |
555 { | |
556 if (it->second == NULL) | |
557 { | |
558 std::list<std::string> children; | |
559 index_.GetChildren(children, it->first); | |
560 | |
561 std::auto_ptr<ArchiveIndex> child(new ArchiveIndex(index_, GetChildResourceType(level_))); | |
562 | |
563 for (std::list<std::string>::const_iterator | |
564 it2 = children.begin(); it2 != children.end(); ++it2) | |
565 { | |
566 child->AddResourceToExpand(*it2); | |
567 } | |
568 | |
569 it->second = child.release(); | |
570 } | |
571 | |
572 assert(it->second != NULL); | |
573 it->second->Expand(); | |
574 } | |
575 } | |
576 | |
577 | |
578 void ComputeStatistics(uint64_t& totalSize, | |
579 unsigned int& totalInstances) | |
580 { | |
581 for (Resources::iterator it = resources_.begin(); | |
582 it != resources_.end(); ++it) | |
583 { | |
584 if (level_ == ResourceType_Instance) | |
585 { | |
586 FileInfo dicom; | |
587 if (index_.LookupAttachment(dicom, it->first, FileContentType_Dicom)) | |
588 { | |
589 totalSize += dicom.GetUncompressedSize(); | |
590 totalInstances ++; | |
591 } | |
592 } | |
593 else | |
594 { | |
595 if (it->second == NULL) | |
596 { | |
597 // The method "Expand" was not called | |
598 throw OrthancException(ErrorCode_InternalError); | |
599 } | |
600 | |
601 it->second->ComputeStatistics(totalSize, totalInstances); | |
602 } | |
603 } | |
604 } | |
605 | |
606 | |
607 void Print(std::ostream& o, | |
608 const std::string& indent = "") const | |
609 { | |
610 for (Resources::const_iterator it = resources_.begin(); | |
611 it != resources_.end(); ++it) | |
612 { | |
613 o << indent << it->first << std::endl; | |
614 if (it->second == NULL) | |
615 { | |
616 if (level_ != ResourceType_Instance) | |
617 { | |
618 o << indent << " *" << std::endl; | |
619 } | |
620 } | |
621 else | |
622 { | |
623 it->second->Print(o, indent + " "); | |
624 } | |
625 } | |
626 } | |
627 }; | |
628 } | |
629 | |
630 | |
631 static void CreateBatchArchive(RestApiPostCall& call) | |
632 { | |
633 ServerIndex& index = OrthancRestApi::GetIndex(call); | |
634 | |
635 Json::Value resources; | |
636 if (call.ParseJsonRequest(resources) && | |
637 resources.type() == Json::arrayValue) | |
638 { | |
639 ArchiveIndex archive(index, ResourceType_Patient); // root | |
640 | |
641 for (Json::Value::ArrayIndex i = 0; i < resources.size(); i++) | |
642 { | |
643 if (resources[i].type() != Json::stringValue) | |
644 { | |
645 return; // Bad request | |
646 } | |
647 | |
648 Resource resource(index, resources[i].asString()); | |
649 archive.Add(resource); | |
650 } | |
651 | |
652 archive.Expand(); | |
653 | |
654 archive.Print(std::cout); | |
655 | |
656 uint64_t totalSize = 0; | |
657 unsigned int totalInstances = 0; | |
658 archive.ComputeStatistics(totalSize, totalInstances); | |
659 | |
660 std::cout << totalSize << " " << totalInstances << std::endl; | |
661 } | |
662 } | |
663 | |
664 | |
665 | |
371 void OrthancRestApi::RegisterArchive() | 666 void OrthancRestApi::RegisterArchive() |
372 { | 667 { |
373 Register("/patients/{id}/archive", GetArchive<ResourceType_Patient>); | 668 Register("/patients/{id}/archive", GetArchive<ResourceType_Patient>); |
374 Register("/studies/{id}/archive", GetArchive<ResourceType_Study>); | 669 Register("/studies/{id}/archive", GetArchive<ResourceType_Study>); |
375 Register("/series/{id}/archive", GetArchive<ResourceType_Series>); | 670 Register("/series/{id}/archive", GetArchive<ResourceType_Series>); |
376 | 671 |
377 Register("/patients/{id}/media", GetMediaArchive); | 672 Register("/patients/{id}/media", GetMediaArchive); |
378 Register("/studies/{id}/media", GetMediaArchive); | 673 Register("/studies/{id}/media", GetMediaArchive); |
379 Register("/series/{id}/media", GetMediaArchive); | 674 Register("/series/{id}/media", GetMediaArchive); |
675 | |
676 Register("/tools/archive", CreateBatchArchive); | |
380 } | 677 } |
381 } | 678 } |