# HG changeset patch # User Sebastien Jodogne # Date 1478537050 -3600 # Node ID 0c09d1af22f3fb8c633f0edf0c7d4d3da1253b83 # Parent 9329ba17a06977b5cca00b1602539cb10a86c776 "/tools/invalidate-tags" to invalidate the JSON summary of all the DICOM files diff -r 9329ba17a069 -r 0c09d1af22f3 Core/FileStorage/FilesystemStorage.cpp --- a/Core/FileStorage/FilesystemStorage.cpp Mon Nov 07 15:13:16 2016 +0100 +++ b/Core/FileStorage/FilesystemStorage.cpp Mon Nov 07 17:44:10 2016 +0100 @@ -223,9 +223,9 @@ void FilesystemStorage::Remove(const std::string& uuid, - FileContentType /*type*/) + FileContentType type) { - LOG(INFO) << "Deleting attachment \"" << uuid << "\""; + LOG(INFO) << "Deleting attachment \"" << uuid << "\" of type " << static_cast(type); namespace fs = boost::filesystem; diff -r 9329ba17a069 -r 0c09d1af22f3 Core/Toolbox.cpp --- a/Core/Toolbox.cpp Mon Nov 07 15:13:16 2016 +0100 +++ b/Core/Toolbox.cpp Mon Nov 07 17:44:10 2016 +0100 @@ -844,6 +844,23 @@ } + bool Toolbox::IsAsciiString(const void* data, + size_t size) + { + const uint8_t* p = reinterpret_cast(data); + + for (size_t i = 0; i < size; i++, p++) + { + if (*p > 127 || (*p != 0 && iscntrl(*p))) + { + return false; + } + } + + return true; + } + + std::string Toolbox::ConvertToAscii(const std::string& source) { std::string result; diff -r 9329ba17a069 -r 0c09d1af22f3 Core/Toolbox.h --- a/Core/Toolbox.h Mon Nov 07 15:13:16 2016 +0100 +++ b/Core/Toolbox.h Mon Nov 07 17:44:10 2016 +0100 @@ -163,6 +163,9 @@ std::string ConvertFromUtf8(const std::string& source, Encoding targetEncoding); + bool IsAsciiString(const void* data, + size_t size); + std::string ConvertToAscii(const std::string& source); std::string StripSpaces(const std::string& source); diff -r 9329ba17a069 -r 0c09d1af22f3 NEWS --- a/NEWS Mon Nov 07 15:13:16 2016 +0100 +++ b/NEWS Mon Nov 07 17:44:10 2016 +0100 @@ -10,10 +10,12 @@ REST API -------- +* "/tools/invalidate-tags" to invalidate the JSON summary of all the DICOM files + (useful if private tags are registered, or if changing the default encoding) * "Permissive" flag for URI "/modalities/{...}/store" to ignore C-Store transfer errors * "Asynchronous" flag for URIs "/modalities/{...}/store" and "/peers/{...}/store" to avoid waiting for the completion of image transfers -* Possibility to DELETE "dicom-as-json" attachments to reconstruct them +* Possibility to DELETE "dicom-as-json" attachments to reconstruct the JSON summaries Plugins ------- diff -r 9329ba17a069 -r 0c09d1af22f3 OrthancServer/FromDcmtkBridge.cpp --- a/OrthancServer/FromDcmtkBridge.cpp Mon Nov 07 15:13:16 2016 +0100 +++ b/OrthancServer/FromDcmtkBridge.cpp Mon Nov 07 17:44:10 2016 +0100 @@ -423,7 +423,7 @@ if (maxStringLength != 0 && utf8.size() > maxStringLength) { - return new DicomValue; // Create a NULL value + return new DicomValue; // Too long, create a NULL value } else { @@ -432,6 +432,47 @@ } } + + if (element.getVR() == EVR_UN) + { + // Unknown value representation: Lookup in the dictionary. This + // is notably the case for private tags registered with the + // "Dictionary" configuration option. + DictionaryLocker locker; + + const DcmDictEntry* entry = locker->findEntry(element.getTag().getXTag(), + element.getTag().getPrivateCreator()); + if (entry != NULL && + entry->getVR().isaString()) + { + Uint8* data = NULL; + + // At (*), we do not try and convert to UTF-8, as nothing says + // the encoding of the private tag is the same as that of the + // remaining of the DICOM dataset. Only go for ASCII strings. + + if (element.getUint8Array(data) == EC_Normal && + Toolbox::IsAsciiString(data, element.getLength())) // (*) + { + if (data == NULL) + { + return new DicomValue("", false); // Empty string + } + else if (maxStringLength != 0 && + element.getLength() > maxStringLength) + { + return new DicomValue; // Too long, create a NULL value + } + else + { + std::string s(reinterpret_cast(data), element.getLength()); + return new DicomValue(s, false); + } + } + } + } + + try { // http://support.dcmtk.org/docs/dcvr_8h-source.html @@ -758,7 +799,8 @@ if (element.isLeaf()) { - std::auto_ptr v(FromDcmtkBridge::ConvertLeafElement(element, flags, maxStringLength, encoding)); + // The "0" below lets "LeafValueToJson()" take care of "TooLong" values + std::auto_ptr v(FromDcmtkBridge::ConvertLeafElement(element, flags, 0, encoding)); LeafValueToJson(target, *v, format, flags, maxStringLength); } else diff -r 9329ba17a069 -r 0c09d1af22f3 OrthancServer/OrthancRestApi/OrthancRestResources.cpp --- a/OrthancServer/OrthancRestApi/OrthancRestResources.cpp Mon Nov 07 15:13:16 2016 +0100 +++ b/OrthancServer/OrthancRestApi/OrthancRestResources.cpp Mon Nov 07 17:44:10 2016 +0100 @@ -1354,6 +1354,32 @@ } + static void InvalidateTags(RestApiPostCall& call) + { + ServerIndex& index = OrthancRestApi::GetIndex(call); + + // Loop over the instances, grouping them by parent studies so as + // to avoid large memory consumption + std::list studies; + index.GetAllUuids(studies, ResourceType_Study); + + for (std::list::const_iterator + study = studies.begin(); study != studies.end(); ++study) + { + std::list instances; + index.GetChildInstances(instances, *study); + + for (std::list::const_iterator + instance = instances.begin(); instance != instances.end(); ++instance) + { + index.DeleteAttachment(*instance, FileContentType_DicomAsJson); + } + } + + call.GetOutput().AnswerBuffer("", "text/plain"); + } + + void OrthancRestApi::RegisterResources() { Register("/instances", ListResources); @@ -1428,6 +1454,7 @@ Register("/{resourceType}/{id}/attachments/{name}/uncompress", ChangeAttachmentCompression); Register("/{resourceType}/{id}/attachments/{name}/verify-md5", VerifyAttachment); + Register("/tools/invalidate-tags", InvalidateTags); Register("/tools/lookup", Lookup); Register("/tools/find", Find); diff -r 9329ba17a069 -r 0c09d1af22f3 OrthancServer/ServerToolbox.cpp --- a/OrthancServer/ServerToolbox.cpp Mon Nov 07 15:13:16 2016 +0100 +++ b/OrthancServer/ServerToolbox.cpp Mon Nov 07 17:44:10 2016 +0100 @@ -334,6 +334,13 @@ { // WARNING: The database should be locked with a transaction! + // TODO: This function might consume much memory if level == + // ResourceType_Instance. To improve this, first download the + // list of studies, then remove the instances for each single + // study (check out OrthancRestApi::InvalidateTags for an + // example). Take this improvement into consideration for the + // next upgrade of the database schema. + const char* plural = NULL; switch (level) diff -r 9329ba17a069 -r 0c09d1af22f3 Resources/Configuration.json --- a/Resources/Configuration.json Mon Nov 07 15:13:16 2016 +0100 +++ b/Resources/Configuration.json Mon Nov 07 17:44:10 2016 +0100 @@ -326,6 +326,7 @@ // possibly the Private Creator (for private tags). "Dictionary" : { // "0014,1020" : [ "DA", "ValidationExpiryDate", 1, 1 ] - // "00e1,10c2" : [ "SH", "PET-CT Multi Modality Name", 1, 1, "ELSCINT1" ] + // "00e1,10c2" : [ "UI", "PET-CT Multi Modality Name", 1, 1, "ELSCINT1" ], + // "7053,1003" : [ "ST", "Original Image Filename", 1, 1, "Philips PET Private Group" ] } }