Mercurial > hg > orthanc
changeset 250:f23318b11b39
creation of zip files
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Wed, 05 Dec 2012 12:29:10 +0100 |
parents | 5694365ecb96 |
children | 4dc9d00c359c |
files | Core/Compression/HierarchicalZipWriter.cpp NEWS OrthancExplorer/explorer.html OrthancExplorer/explorer.js OrthancServer/OrthancRestApi.cpp OrthancServer/ServerEnumerations.cpp OrthancServer/ServerEnumerations.h |
diffstat | 7 files changed, 298 insertions(+), 102 deletions(-) [+] |
line wrap: on
line diff
--- 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; } }
--- 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) ==========================
--- 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 @@ <p> <a href="#find-patients" data-role="button" data-icon="search">Go to patient finder</a> <a href="#" data-role="button" data-icon="delete" id="patient-delete">Delete this patient</a> + <a href="#" data-role="button" data-icon="gear" id="patient-archive">Download ZIP</a> </p> </div> </div> @@ -115,6 +116,7 @@ </ul> <p> <a href="#" data-role="button" data-icon="delete" id="study-delete">Delete this study</a> + <a href="#" data-role="button" data-icon="gear" id="study-archive">Download ZIP</a> </p> </div> </div> @@ -148,8 +150,9 @@ </ul> <p> <a href="#" data-role="button" data-icon="delete" id="series-delete">Delete this series</a> - <a href="#" data-role="button" data-icon="arrow-d" id="series-preview">Preview this series</a> - <a href="#" data-role="button" data-icon="arrow-d" id="series-store">Store in another DICOM modality</a> + <a href="#" data-role="button" data-icon="search" id="series-preview">Preview this series</a> + <a href="#" data-role="button" data-icon="forward" id="series-store">Store in another DICOM modality</a> + <a href="#" data-role="button" data-icon="gear" id="series-archive">Download ZIP</a> </p> </div> </div> @@ -185,8 +188,8 @@ <a href="#" data-role="button" data-icon="delete" id="instance-delete">Delete this instance</a> <a href="#" data-role="button" data-icon="arrow-d" id="instance-download-dicom">Download the DICOM file</a> <a href="#" data-role="button" data-icon="arrow-d" id="instance-download-json">Download the JSON file</a> - <a href="#" data-role="button" data-icon="arrow-d" id="instance-preview">Preview the instance</a> - <a href="#" data-role="button" data-icon="arrow-d" id="instance-store">Store in another DICOM modality</a> + <a href="#" data-role="button" data-icon="search" id="instance-preview">Preview the instance</a> + <a href="#" data-role="button" data-icon="forward" id="instance-store">Store in another DICOM modality</a> </p> </div> </div>
--- 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'; +}); +
--- 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 <enum ResourceType resourceType> + 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<ResourceType_Series>); Register("/studies/{id}", DeleteSingleResource<ResourceType_Study>); Register("/studies/{id}", GetSingleResource<ResourceType_Study>); - Register("/patients/{id}/archive", GetPatientArchive); + + Register("/patients/{id}/archive", GetArchive<ResourceType_Patient>); + Register("/studies/{id}/archive", GetArchive<ResourceType_Study>); + Register("/series/{id}/archive", GetArchive<ResourceType_Series>); Register("/instances/{id}/file", GetInstanceFile); Register("/instances/{id}/tags", GetInstanceTags<false>);
--- 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); } } }
--- 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); }