# HG changeset patch # User Sebastien Jodogne # Date 1354706950 -3600 # Node ID f23318b11b39e5451e0876cc6eeaeab1dff9aae5 # Parent 5694365ecb969a562a410836c402403f15741f26 creation of zip files diff -r 5694365ecb96 -r f23318b11b39 Core/Compression/HierarchicalZipWriter.cpp --- a/Core/Compression/HierarchicalZipWriter.cpp Wed Dec 05 09:28:06 2012 +0100 +++ b/Core/Compression/HierarchicalZipWriter.cpp Wed Dec 05 12:29:10 2012 +0100 @@ -48,10 +48,14 @@ result.reserve(source.size()); for (size_t i = 0; i < source.size(); i++) { - if (source[i] < 128 && - source[i] >= 0) + char c = source[i]; + if (c == '^') + c = ' '; + + if (c < 128 && + c >= 0) { - if (isspace(source[i])) + if (isspace(c)) { if (!lastSpace) { @@ -59,11 +63,11 @@ result.push_back(' '); } } - else if (isalnum(source[i]) || - source[i] == '.' || - source[i] == '_') + else if (isalnum(c) || + c == '.' || + c == '_') { - result.push_back(source[i]); + result.push_back(c); lastSpace = false; } } diff -r 5694365ecb96 -r f23318b11b39 NEWS --- a/NEWS Wed Dec 05 09:28:06 2012 +0100 +++ b/NEWS Wed Dec 05 12:29:10 2012 +0100 @@ -1,6 +1,8 @@ Pending changes in the mainline =============================== +* Download archives of patients, studies and serie as ZIP files + Version 0.3.0 (2012/11/30) ========================== diff -r 5694365ecb96 -r f23318b11b39 OrthancExplorer/explorer.html --- a/OrthancExplorer/explorer.html Wed Dec 05 09:28:06 2012 +0100 +++ b/OrthancExplorer/explorer.html Wed Dec 05 12:29:10 2012 +0100 @@ -84,6 +84,7 @@

Go to patient finder Delete this patient + Download ZIP

@@ -115,6 +116,7 @@

Delete this study + Download ZIP

@@ -148,8 +150,9 @@

Delete this series - Preview this series - Store in another DICOM modality + Preview this series + Store in another DICOM modality + Download ZIP

@@ -185,8 +188,8 @@ Delete this instance Download the DICOM file Download the JSON file - Preview the instance - Store in another DICOM modality + Preview the instance + Store in another DICOM modality

diff -r 5694365ecb96 -r f23318b11b39 OrthancExplorer/explorer.js --- a/OrthancExplorer/explorer.js Wed Dec 05 09:28:06 2012 +0100 +++ b/OrthancExplorer/explorer.js Wed Dec 05 12:29:10 2012 +0100 @@ -626,6 +626,7 @@ }); + $('#instance-preview').live('click', function(e) { if ($.mobile.pageData) { GetSingleResource('instances', $.mobile.pageData.uuid + '/frames', function(frames) { @@ -768,3 +769,20 @@ else $('.tag-name').hide(); }); + + +$('#patient-archive').live('click', function(e) { + e.preventDefault(); //stop the browser from following + window.location.href = '../patients/' + $.mobile.pageData.uuid + '/archive'; +}); + +$('#study-archive').live('click', function(e) { + e.preventDefault(); //stop the browser from following + window.location.href = '../studies/' + $.mobile.pageData.uuid + '/archive'; +}); + +$('#series-archive').live('click', function(e) { + e.preventDefault(); //stop the browser from following + window.location.href = '../series/' + $.mobile.pageData.uuid + '/archive'; +}); + diff -r 5694365ecb96 -r f23318b11b39 OrthancServer/OrthancRestApi.cpp --- a/OrthancServer/OrthancRestApi.cpp Wed Dec 05 09:28:06 2012 +0100 +++ b/OrthancServer/OrthancRestApi.cpp Wed Dec 05 12:29:10 2012 +0100 @@ -296,7 +296,6 @@ static void GetSystemInformation(RestApi::GetCall& call) { - RETRIEVE_CONTEXT(call); Json::Value result = Json::objectValue; result["Version"] = ORTHANC_VERSION; @@ -353,57 +352,182 @@ // Download of ZIP files ---------------------------------------------------- - static void GetPatientArchive(RestApi::GetCall& call) + + static std::string GetDirectoryNameInArchive(const Json::Value& resource, + ResourceType resourceType) + { + switch (resourceType) + { + case ResourceType_Patient: + { + std::string p = resource["MainDicomTags"]["PatientID"].asString(); + std::string n = resource["MainDicomTags"]["PatientName"].asString(); + return p + " " + n; + } + + case ResourceType_Study: + return resource["MainDicomTags"]["StudyDescription"].asString(); + + case ResourceType_Series: + return resource["MainDicomTags"]["SeriesDescription"].asString(); + + default: + throw OrthancException(ErrorCode_InternalError); + } + } + + static bool CreateRootDirectoryInArchive(HierarchicalZipWriter& writer, + ServerContext& context, + const Json::Value& resource, + ResourceType resourceType) + { + if (resourceType == ResourceType_Patient) + { + return true; + } + + ResourceType parentType = GetParentResourceType(resourceType); + Json::Value parent; + + switch (resourceType) + { + case ResourceType_Study: + { + if (!context.GetIndex().LookupResource(parent, resource["ParentPatient"].asString(), parentType)) + { + return false; + } + + break; + } + + case ResourceType_Series: + if (!context.GetIndex().LookupResource(parent, resource["ParentStudy"].asString(), parentType) || + !CreateRootDirectoryInArchive(writer, context, parent, parentType)) + { + return false; + } + break; + + default: + throw OrthancException(ErrorCode_NotImplemented); + } + + writer.OpenDirectory(GetDirectoryNameInArchive(parent, parentType).c_str()); + return true; + } + + static bool ArchiveInstance(HierarchicalZipWriter& writer, + ServerContext& context, + const std::string& instancePublicId) + { + Json::Value instance; + if (!context.GetIndex().LookupResource(instance, instancePublicId, ResourceType_Instance)) + { + return false; + } + + std::string filename = instance["MainDicomTags"]["SOPInstanceUID"].asString() + ".dcm"; + writer.OpenFile(filename.c_str()); + + std::string dicom; + context.ReadFile(dicom, instancePublicId, FileContentType_Dicom); + writer.Write(dicom); + + return true; + } + + static bool ArchiveInternal(HierarchicalZipWriter& writer, + ServerContext& context, + const std::string& publicId, + ResourceType resourceType, + bool isFirstLevel) + { + Json::Value resource; + if (!context.GetIndex().LookupResource(resource, publicId, resourceType)) + { + return false; + } + + if (isFirstLevel && + !CreateRootDirectoryInArchive(writer, context, resource, resourceType)) + { + return false; + } + + writer.OpenDirectory(GetDirectoryNameInArchive(resource, resourceType).c_str()); + + switch (resourceType) + { + case ResourceType_Patient: + for (size_t i = 0; i < resource["Studies"].size(); i++) + { + std::string studyId = resource["Studies"][i].asString(); + if (!ArchiveInternal(writer, context, studyId, ResourceType_Study, false)) + { + return false; + } + } + break; + + case ResourceType_Study: + for (size_t i = 0; i < resource["Series"].size(); i++) + { + std::string seriesId = resource["Series"][i].asString(); + if (!ArchiveInternal(writer, context, seriesId, ResourceType_Series, false)) + { + return false; + } + } + break; + + case ResourceType_Series: + for (size_t i = 0; i < resource["Instances"].size(); i++) + { + if (!ArchiveInstance(writer, context, resource["Instances"][i].asString())) + { + return false; + } + } + break; + + default: + throw OrthancException(ErrorCode_InternalError); + } + + writer.CloseDirectory(); + return true; + } + + template + static void GetArchive(RestApi::GetCall& call) { RETRIEVE_CONTEXT(call); - Json::Value patient; - if (!context.GetIndex().LookupResource(patient, call.GetUriComponent("id", ""), ResourceType_Patient)) - { - return; - } - + // Create a RAII for the temporary file to manage the ZIP file Toolbox::TemporaryFile tmp; + std::string id = call.GetUriComponent("id", ""); { + // Create a ZIP writer HierarchicalZipWriter writer(tmp.GetPath().c_str()); - - for (size_t i = 0; i < patient["Studies"].size(); i++) - { - Json::Value study; - if (context.GetIndex().LookupResource(study, patient["Studies"][i].asString(), ResourceType_Study)) - { - writer.OpenDirectory(study["MainDicomTags"]["StudyDescription"].asString().c_str()); - for (size_t i = 0; i < study["Series"].size(); i++) - { - Json::Value series; - if (context.GetIndex().LookupResource(series, study["Series"][i].asString(), ResourceType_Series)) - { - std::string m = series["MainDicomTags"]["Modality"].asString(); - std::string s = series["MainDicomTags"]["SeriesDescription"].asString(); - writer.OpenDirectory((m + " " + s).c_str()); - - for (size_t i = 0; i < series["Instances"].size(); i++) - { - Json::Value instance; - if (context.GetIndex().LookupResource(instance, series["Instances"][i].asString(), ResourceType_Instance)) - { - writer.OpenFile(instance["MainDicomTags"]["SOPInstanceUID"].asString().c_str()); - } - } - - writer.CloseDirectory(); - } - } - - writer.CloseDirectory(); - } + // Store the requested resource into the ZIP + if (!ArchiveInternal(writer, context, id, resourceType, true)) + { + return; } } - + + // Prepare the sending of the ZIP file FilesystemHttpSender sender(tmp.GetPath().c_str()); + sender.SetContentType("application/zip"); + sender.SetDownloadFilename(id + ".zip"); + + // Send the ZIP call.GetOutput().AnswerFile(sender); + + // The temporary file is automatically removed thanks to the RAII } @@ -710,7 +834,10 @@ Register("/series/{id}", GetSingleResource); Register("/studies/{id}", DeleteSingleResource); Register("/studies/{id}", GetSingleResource); - Register("/patients/{id}/archive", GetPatientArchive); + + Register("/patients/{id}/archive", GetArchive); + Register("/studies/{id}/archive", GetArchive); + Register("/series/{id}/archive", GetArchive); Register("/instances/{id}/file", GetInstanceFile); Register("/instances/{id}/tags", GetInstanceTags); diff -r 5694365ecb96 -r f23318b11b39 OrthancServer/ServerEnumerations.cpp --- a/OrthancServer/ServerEnumerations.cpp Wed Dec 05 09:28:06 2012 +0100 +++ b/OrthancServer/ServerEnumerations.cpp Wed Dec 05 12:29:10 2012 +0100 @@ -39,20 +39,20 @@ { switch (type) { - case ResourceType_Patient: - return "Patient"; + case ResourceType_Patient: + return "Patient"; - case ResourceType_Study: - return "Study"; + case ResourceType_Study: + return "Study"; - case ResourceType_Series: - return "Series"; + case ResourceType_Series: + return "Series"; - case ResourceType_Instance: - return "Instance"; + case ResourceType_Instance: + return "Instance"; - default: - throw OrthancException(ErrorCode_ParameterOutOfRange); + default: + throw OrthancException(ErrorCode_ParameterOutOfRange); } } @@ -61,20 +61,20 @@ { switch (type) { - case ResourceType_Patient: - return "/patients/" + publicId; + case ResourceType_Patient: + return "/patients/" + publicId; - case ResourceType_Study: - return "/studies/" + publicId; + case ResourceType_Study: + return "/studies/" + publicId; - case ResourceType_Series: - return "/series/" + publicId; + case ResourceType_Series: + return "/series/" + publicId; - case ResourceType_Instance: - return "/instances/" + publicId; + case ResourceType_Instance: + return "/instances/" + publicId; - default: - throw OrthancException(ErrorCode_ParameterOutOfRange); + default: + throw OrthancException(ErrorCode_ParameterOutOfRange); } } @@ -82,20 +82,20 @@ { switch (status) { - case SeriesStatus_Complete: - return "Complete"; + case SeriesStatus_Complete: + return "Complete"; - case SeriesStatus_Missing: - return "Missing"; + case SeriesStatus_Missing: + return "Missing"; - case SeriesStatus_Inconsistent: - return "Inconsistent"; + case SeriesStatus_Inconsistent: + return "Inconsistent"; - case SeriesStatus_Unknown: - return "Unknown"; + case SeriesStatus_Unknown: + return "Unknown"; - default: - throw OrthancException(ErrorCode_ParameterOutOfRange); + default: + throw OrthancException(ErrorCode_ParameterOutOfRange); } } @@ -103,17 +103,17 @@ { switch (status) { - case StoreStatus_Success: - return "Success"; + case StoreStatus_Success: + return "Success"; - case StoreStatus_AlreadyStored: - return "AlreadyStored"; + case StoreStatus_AlreadyStored: + return "AlreadyStored"; - case StoreStatus_Failure: - return "Failure"; + case StoreStatus_Failure: + return "Failure"; - default: - throw OrthancException(ErrorCode_ParameterOutOfRange); + default: + throw OrthancException(ErrorCode_ParameterOutOfRange); } } @@ -122,23 +122,61 @@ { switch (type) { - case ChangeType_CompletedSeries: - return "CompletedSeries"; + case ChangeType_CompletedSeries: + return "CompletedSeries"; + + case ChangeType_NewInstance: + return "NewInstance"; + + case ChangeType_NewPatient: + return "NewPatient"; + + case ChangeType_NewSeries: + return "NewSeries"; - case ChangeType_NewInstance: - return "NewInstance"; + case ChangeType_NewStudy: + return "NewStudy"; + + default: + throw OrthancException(ErrorCode_ParameterOutOfRange); + } + } - case ChangeType_NewPatient: - return "NewPatient"; + + ResourceType GetParentResourceType(ResourceType type) + { + switch (type) + { + case ResourceType_Study: + return ResourceType_Patient; - case ChangeType_NewSeries: - return "NewSeries"; + case ResourceType_Series: + return ResourceType_Study; + + case ResourceType_Instance: + return ResourceType_Series; + + default: + throw OrthancException(ErrorCode_ParameterOutOfRange); + } + } + - case ChangeType_NewStudy: - return "NewStudy"; + ResourceType GetChildResourceType(ResourceType type) + { + switch (type) + { + case ResourceType_Patient: + return ResourceType_Study; - default: - throw OrthancException(ErrorCode_ParameterOutOfRange); + case ResourceType_Study: + return ResourceType_Series; + + case ResourceType_Series: + return ResourceType_Instance; + + default: + throw OrthancException(ErrorCode_ParameterOutOfRange); } } } diff -r 5694365ecb96 -r f23318b11b39 OrthancServer/ServerEnumerations.h --- a/OrthancServer/ServerEnumerations.h Wed Dec 05 09:28:06 2012 +0100 +++ b/OrthancServer/ServerEnumerations.h Wed Dec 05 12:29:10 2012 +0100 @@ -97,4 +97,8 @@ const char* ToString(StoreStatus status); const char* ToString(ChangeType type); + + ResourceType GetParentResourceType(ResourceType type); + + ResourceType GetChildResourceType(ResourceType type); }