# HG changeset patch # User Sebastien Jodogne # Date 1443626319 -7200 # Node ID de1413733c97c96592d2dd0cb2b36e793164d0e7 # Parent 9e875db36aefecdc03bc133970fb071870d8fecc reconstructing main dicom tags diff -r 9e875db36aef -r de1413733c97 OrthancServer/DatabaseWrapper.cpp --- a/OrthancServer/DatabaseWrapper.cpp Wed Sep 30 14:04:53 2015 +0200 +++ b/OrthancServer/DatabaseWrapper.cpp Wed Sep 30 17:18:39 2015 +0200 @@ -465,6 +465,22 @@ } + void DatabaseWrapper::ClearMainDicomTags(int64_t id) + { + { + SQLite::Statement s(db_, SQLITE_FROM_HERE, "DELETE FROM DicomIdentifiers WHERE id=?"); + s.BindInt64(0, id); + s.Run(); + } + + { + SQLite::Statement s(db_, SQLITE_FROM_HERE, "DELETE FROM MainDicomTags WHERE id=?"); + s.BindInt64(0, id); + s.Run(); + } + } + + static void SetMainDicomTagsInternal(SQLite::Statement& s, int64_t id, const DicomTag& tag, @@ -836,28 +852,6 @@ } - void DatabaseWrapper::ExecuteUpgrade5To6(IStorageArea& storageArea) - { - printf("ICI\n"); - - std::auto_ptr transaction(StartTransaction()); - transaction->Begin(); - - std::list studies; - GetAllPublicIds(studies, ResourceType_Study); - - for (std::list::const_iterator - it = studies.begin(); it != studies.end(); it++) - { - printf("[%s]\n", it->c_str()); - } - - SetGlobalProperty(GlobalProperty_DatabaseSchemaVersion, "6"); - - transaction->Commit(); - } - - void DatabaseWrapper::Upgrade(unsigned int targetVersion, IStorageArea& storageArea) { @@ -893,7 +887,13 @@ if (version_ == 5) { LOG(WARNING) << "Upgrading database version from 5 to 6"; - ExecuteUpgrade5To6(storageArea); + // No change in the DB schema, the step from version 5 to 6 only + // consists in reconstructing the main DICOM tags information. + db_.BeginTransaction(); + SetGlobalProperty(GlobalProperty_DatabaseSchemaVersion, "6"); + SetGlobalProperty(GlobalProperty_ReconstructStudiesTags, "1"); + SetGlobalProperty(GlobalProperty_ReconstructSeriesTags, "1"); + db_.CommitTransaction(); version_ = 6; } } diff -r 9e875db36aef -r de1413733c97 OrthancServer/DatabaseWrapper.h --- a/OrthancServer/DatabaseWrapper.h Wed Sep 30 14:04:53 2015 +0200 +++ b/OrthancServer/DatabaseWrapper.h Wed Sep 30 17:18:39 2015 +0200 @@ -71,8 +71,6 @@ void ClearTable(const std::string& tableName); - void ExecuteUpgrade5To6(IStorageArea& storageArea); - public: DatabaseWrapper(const std::string& path); @@ -132,6 +130,8 @@ int64_t id, FileContentType contentType); + virtual void ClearMainDicomTags(int64_t id); + virtual void SetMainDicomTag(int64_t id, const DicomTag& tag, const std::string& value); diff -r 9e875db36aef -r de1413733c97 OrthancServer/IDatabaseWrapper.h --- a/OrthancServer/IDatabaseWrapper.h Wed Sep 30 14:04:53 2015 +0200 +++ b/OrthancServer/IDatabaseWrapper.h Wed Sep 30 17:18:39 2015 +0200 @@ -168,6 +168,8 @@ virtual void SetGlobalProperty(GlobalProperty property, const std::string& value) = 0; + virtual void ClearMainDicomTags(int64_t id) = 0; + virtual void SetMainDicomTag(int64_t id, const DicomTag& tag, const std::string& value) = 0; diff -r 9e875db36aef -r de1413733c97 OrthancServer/Internals/StoreScp.cpp --- a/OrthancServer/Internals/StoreScp.cpp Wed Sep 30 14:04:53 2015 +0200 +++ b/OrthancServer/Internals/StoreScp.cpp Wed Sep 30 17:18:39 2015 +0200 @@ -211,7 +211,7 @@ if (e.GetErrorCode() == ErrorCode_InexistentTag) { - LogMissingRequiredTag(summary); + Toolbox::LogMissingRequiredTag(summary); } else { diff -r 9e875db36aef -r de1413733c97 OrthancServer/OrthancRestApi/OrthancRestResources.cpp --- a/OrthancServer/OrthancRestApi/OrthancRestResources.cpp Wed Sep 30 14:04:53 2015 +0200 +++ b/OrthancServer/OrthancRestApi/OrthancRestResources.cpp Wed Sep 30 17:18:39 2015 +0200 @@ -208,7 +208,7 @@ context.ReadJson(full, publicId); Json::Value simplified; - SimplifyTags(simplified, full); + Toolbox::SimplifyTags(simplified, full); call.GetOutput().AnswerJson(simplified); } else @@ -764,7 +764,7 @@ if (simplify) { Json::Value simplified; - SimplifyTags(simplified, sharedTags); + Toolbox::SimplifyTags(simplified, sharedTags); call.GetOutput().AnswerJson(simplified); } else @@ -831,7 +831,7 @@ if (simplify) { Json::Value simplified; - SimplifyTags(simplified, result); + Toolbox::SimplifyTags(simplified, result); call.GetOutput().AnswerJson(simplified); } else @@ -1002,7 +1002,7 @@ if (simplify) { Json::Value simplified; - SimplifyTags(simplified, full); + Toolbox::SimplifyTags(simplified, full); result[*it] = simplified; } else diff -r 9e875db36aef -r de1413733c97 OrthancServer/OrthancRestApi/OrthancRestSystem.cpp --- a/OrthancServer/OrthancRestApi/OrthancRestSystem.cpp Wed Sep 30 14:04:53 2015 +0200 +++ b/OrthancServer/OrthancRestApi/OrthancRestSystem.cpp Wed Sep 30 17:18:39 2015 +0200 @@ -53,9 +53,7 @@ { Json::Value result = Json::objectValue; - std::string dbVersion = OrthancRestApi::GetIndex(call).GetGlobalProperty(GlobalProperty_DatabaseSchemaVersion, "0"); - - result["DatabaseVersion"] = boost::lexical_cast(dbVersion); + result["DatabaseVersion"] = OrthancRestApi::GetIndex(call).GetDatabaseVersion(); result["DicomAet"] = Configuration::GetGlobalStringParameter("DicomAet", "ORTHANC"); result["DicomPort"] = Configuration::GetGlobalIntegerParameter("DicomPort", 4242); result["HttpPort"] = Configuration::GetGlobalIntegerParameter("HttpPort", 8042); diff -r 9e875db36aef -r de1413733c97 OrthancServer/ParsedDicomFile.cpp --- a/OrthancServer/ParsedDicomFile.cpp Wed Sep 30 14:04:53 2015 +0200 +++ b/OrthancServer/ParsedDicomFile.cpp Wed Sep 30 17:18:39 2015 +0200 @@ -1370,7 +1370,7 @@ { Json::Value tmp; FromDcmtkBridge::ToJson(tmp, *pimpl_->file_->getDataset()); - SimplifyTags(target, tmp); + Toolbox::SimplifyTags(target, tmp); } else { diff -r 9e875db36aef -r de1413733c97 OrthancServer/PrepareDatabase.sql --- a/OrthancServer/PrepareDatabase.sql Wed Sep 30 14:04:53 2015 +0200 +++ b/OrthancServer/PrepareDatabase.sql Wed Sep 30 17:18:39 2015 +0200 @@ -123,4 +123,4 @@ -- Set the version of the database schema -- The "1" corresponds to the "GlobalProperty_DatabaseSchemaVersion" enumeration -INSERT INTO GlobalProperties VALUES (1, "5"); +INSERT INTO GlobalProperties VALUES (1, "6"); diff -r 9e875db36aef -r de1413733c97 OrthancServer/ServerContext.cpp --- a/OrthancServer/ServerContext.cpp Wed Sep 30 14:04:53 2015 +0200 +++ b/OrthancServer/ServerContext.cpp Wed Sep 30 17:18:39 2015 +0200 @@ -189,7 +189,7 @@ resultPublicId = hasher.HashInstance(); Json::Value simplifiedTags; - SimplifyTags(simplifiedTags, dicom.GetJson()); + Toolbox::SimplifyTags(simplifiedTags, dicom.GetJson()); // Test if the instance must be filtered out bool accepted = true; @@ -299,7 +299,7 @@ { if (e.GetErrorCode() == ErrorCode_InexistentTag) { - LogMissingRequiredTag(dicom.GetSummary()); + Toolbox::LogMissingRequiredTag(dicom.GetSummary()); } throw; diff -r 9e875db36aef -r de1413733c97 OrthancServer/ServerEnumerations.h --- a/OrthancServer/ServerEnumerations.h Wed Sep 30 14:04:53 2015 +0200 +++ b/OrthancServer/ServerEnumerations.h Wed Sep 30 17:18:39 2015 +0200 @@ -109,9 +109,13 @@ enum GlobalProperty { - GlobalProperty_DatabaseSchemaVersion = 1, + GlobalProperty_DatabaseSchemaVersion = 1, // Unused in the Orthanc core as of Orthanc 0.9.5 GlobalProperty_FlushSleep = 2, - GlobalProperty_AnonymizationSequence = 3 + GlobalProperty_AnonymizationSequence = 3, + GlobalProperty_ReconstructPatientsTags = 4, + GlobalProperty_ReconstructStudiesTags = 5, + GlobalProperty_ReconstructSeriesTags = 6, + GlobalProperty_ReconstructInstancesTags = 7 }; enum MetadataType diff -r 9e875db36aef -r de1413733c97 OrthancServer/ServerIndex.cpp --- a/OrthancServer/ServerIndex.cpp Wed Sep 30 14:04:53 2015 +0200 +++ b/OrthancServer/ServerIndex.cpp Wed Sep 30 17:18:39 2015 +0200 @@ -40,6 +40,7 @@ #include "ServerIndexChange.h" #include "EmbeddedResources.h" #include "OrthancInitialization.h" +#include "ServerToolbox.h" #include "../Core/Toolbox.h" #include "../Core/Logging.h" #include "../Core/Uuid.h" @@ -490,18 +491,6 @@ - void ServerIndex::SetMainDicomTags(int64_t resource, - const DicomMap& tags) - { - DicomArray flattened(tags); - for (size_t i = 0; i < flattened.GetSize(); i++) - { - const DicomElement& element = flattened.GetElement(i); - db_.SetMainDicomTag(resource, element.GetTag(), element.GetValue().AsString()); - } - } - - int64_t ServerIndex::CreateResource(const std::string& publicId, ResourceType type) { @@ -638,10 +627,7 @@ // Create the instance int64_t instance = CreateResource(hasher.HashInstance(), ResourceType_Instance); - - DicomMap dicom; - dicomSummary.ExtractInstanceInformation(dicom); - SetMainDicomTags(instance, dicom); + Toolbox::SetMainDicomTags(db_, instance, ResourceType_Instance, dicomSummary, true); // Detect up to which level the patient/study/series/instance // hierarchy must be created @@ -693,24 +679,22 @@ if (isNewSeries) { series = CreateResource(hasher.HashSeries(), ResourceType_Series); - dicomSummary.ExtractSeriesInformation(dicom); - SetMainDicomTags(series, dicom); + Toolbox::SetMainDicomTags(db_, series, ResourceType_Series, dicomSummary, true); } // Create the study if needed if (isNewStudy) { study = CreateResource(hasher.HashStudy(), ResourceType_Study); - dicomSummary.ExtractStudyInformation(dicom); - SetMainDicomTags(study, dicom); + Toolbox::SetMainDicomTags(db_, study, ResourceType_Study, dicomSummary, true); + Toolbox::SetMainDicomTags(db_, study, ResourceType_Patient, dicomSummary, false); // New in version 0.9.5 (db v6) } // Create the patient if needed if (isNewPatient) { patient = CreateResource(hasher.HashPatient(), ResourceType_Patient); - dicomSummary.ExtractPatientInformation(dicom); - SetMainDicomTags(patient, dicom); + Toolbox::SetMainDicomTags(db_, patient, ResourceType_Patient, dicomSummary, true); } // Create the parent-to-child links @@ -892,7 +876,8 @@ void ServerIndex::MainDicomTagsToJson(Json::Value& target, - int64_t resourceId) + int64_t resourceId, + ResourceType resourceType) { DicomMap tags; db_.GetMainDicomTags(tags, resourceId); @@ -1033,7 +1018,7 @@ // Record the remaining information result["ID"] = publicId; - MainDicomTagsToJson(result, id); + MainDicomTagsToJson(result, id, type); std::string tmp; @@ -2108,4 +2093,10 @@ return db_.LookupResource(id, type, publicId); } + + unsigned int ServerIndex::GetDatabaseVersion() + { + boost::mutex::scoped_lock lock(mutex_); + return db_.GetDatabaseVersion(); + } } diff -r 9e875db36aef -r de1413733c97 OrthancServer/ServerIndex.h --- a/OrthancServer/ServerIndex.h Wed Sep 30 14:04:53 2015 +0200 +++ b/OrthancServer/ServerIndex.h Wed Sep 30 17:18:39 2015 +0200 @@ -76,7 +76,8 @@ static void UnstableResourcesMonitorThread(ServerIndex* that); void MainDicomTagsToJson(Json::Value& result, - int64_t resourceId); + int64_t resourceId, + ResourceType resourceType); SeriesStatus GetSeriesStatus(int64_t id); @@ -110,9 +111,6 @@ uint64_t IncrementGlobalSequenceInternal(GlobalProperty property); - void SetMainDicomTags(int64_t resource, - const DicomMap& tags); - int64_t CreateResource(const std::string& publicId, ResourceType type); @@ -267,5 +265,7 @@ bool LookupResourceType(ResourceType& type, const std::string& publicId); + + unsigned int GetDatabaseVersion(); }; } diff -r 9e875db36aef -r de1413733c97 OrthancServer/ServerToolbox.cpp --- a/OrthancServer/ServerToolbox.cpp Wed Sep 30 14:04:53 2015 +0200 +++ b/OrthancServer/ServerToolbox.cpp Wed Sep 30 17:18:39 2015 +0200 @@ -35,120 +35,267 @@ #include "../Core/Logging.h" #include "../Core/OrthancException.h" +#include "../Core/DicomFormat/DicomArray.h" +#include "ParsedDicomFile.h" #include namespace Orthanc { - void SimplifyTags(Json::Value& target, - const Json::Value& source) + namespace Toolbox { - assert(source.isObject()); + void SimplifyTags(Json::Value& target, + const Json::Value& source) + { + assert(source.isObject()); - target = Json::objectValue; - Json::Value::Members members = source.getMemberNames(); + target = Json::objectValue; + Json::Value::Members members = source.getMemberNames(); + + for (size_t i = 0; i < members.size(); i++) + { + const Json::Value& v = source[members[i]]; + const std::string& name = v["Name"].asString(); + const std::string& type = v["Type"].asString(); - for (size_t i = 0; i < members.size(); i++) - { - const Json::Value& v = source[members[i]]; - const std::string& name = v["Name"].asString(); - const std::string& type = v["Type"].asString(); + if (type == "String") + { + target[name] = v["Value"].asString(); + } + else if (type == "TooLong" || + type == "Null") + { + target[name] = Json::nullValue; + } + else if (type == "Sequence") + { + const Json::Value& array = v["Value"]; + assert(array.isArray()); - if (type == "String") - { - target[name] = v["Value"].asString(); + Json::Value children = Json::arrayValue; + for (Json::Value::ArrayIndex i = 0; i < array.size(); i++) + { + Json::Value c; + SimplifyTags(c, array[i]); + children.append(c); + } + + target[name] = children; + } + else + { + assert(0); + } } - else if (type == "TooLong" || - type == "Null") + } + + + void LogMissingRequiredTag(const DicomMap& summary) + { + std::string s, t; + + if (summary.HasTag(DICOM_TAG_PATIENT_ID)) { - target[name] = Json::nullValue; + if (t.size() > 0) + t += ", "; + t += "PatientID=" + summary.GetValue(DICOM_TAG_PATIENT_ID).AsString(); } - else if (type == "Sequence") + else { - const Json::Value& array = v["Value"]; - assert(array.isArray()); + if (s.size() > 0) + s += ", "; + s += "PatientID"; + } - Json::Value children = Json::arrayValue; - for (Json::Value::ArrayIndex i = 0; i < array.size(); i++) - { - Json::Value c; - SimplifyTags(c, array[i]); - children.append(c); - } + if (summary.HasTag(DICOM_TAG_STUDY_INSTANCE_UID)) + { + if (t.size() > 0) + t += ", "; + t += "StudyInstanceUID=" + summary.GetValue(DICOM_TAG_STUDY_INSTANCE_UID).AsString(); + } + else + { + if (s.size() > 0) + s += ", "; + s += "StudyInstanceUID"; + } - target[name] = children; + if (summary.HasTag(DICOM_TAG_SERIES_INSTANCE_UID)) + { + if (t.size() > 0) + t += ", "; + t += "SeriesInstanceUID=" + summary.GetValue(DICOM_TAG_SERIES_INSTANCE_UID).AsString(); + } + else + { + if (s.size() > 0) + s += ", "; + s += "SeriesInstanceUID"; + } + + if (summary.HasTag(DICOM_TAG_SOP_INSTANCE_UID)) + { + if (t.size() > 0) + t += ", "; + t += "SOPInstanceUID=" + summary.GetValue(DICOM_TAG_SOP_INSTANCE_UID).AsString(); + } + else + { + if (s.size() > 0) + s += ", "; + s += "SOPInstanceUID"; + } + + if (t.size() == 0) + { + LOG(ERROR) << "Store has failed because all the required tags (" << s << ") are missing (is it a DICOMDIR file?)"; } else { - assert(0); + LOG(ERROR) << "Store has failed because required tags (" << s << ") are missing for the following instance: " << t; + } + } + + + void SetMainDicomTags(IDatabaseWrapper& database, + int64_t resource, + ResourceType level, + const DicomMap& dicomSummary, + bool includeIdentifiers) + { + // WARNING: The database should be locked with a transaction! + + DicomMap tags; + + switch (level) + { + case ResourceType_Patient: + dicomSummary.ExtractPatientInformation(tags); + break; + + case ResourceType_Study: + dicomSummary.ExtractStudyInformation(tags); + break; + + case ResourceType_Series: + dicomSummary.ExtractSeriesInformation(tags); + break; + + case ResourceType_Instance: + dicomSummary.ExtractInstanceInformation(tags); + break; + + default: + throw OrthancException(ErrorCode_InternalError); + } + + DicomArray flattened(tags); + for (size_t i = 0; i < flattened.GetSize(); i++) + { + const DicomElement& element = flattened.GetElement(i); + + if (includeIdentifiers || + !element.GetTag().IsIdentifier()) + { + database.SetMainDicomTag(resource, element.GetTag(), element.GetValue().AsString()); + } + } + } + + + bool FindOneChildInstance(int64_t& result, + IDatabaseWrapper& database, + int64_t resource, + ResourceType type) + { + for (;;) + { + if (type == ResourceType_Instance) + { + result = resource; + return true; + } + + std::list children; + database.GetChildrenInternalId(children, resource); + if (children.empty()) + { + return false; + } + + resource = children.front(); + type = GetChildResourceType(type); + } + } + + + void ReconstructMainDicomTags(IDatabaseWrapper& database, + IStorageArea& storageArea, + ResourceType level) + { + // WARNING: The database should be locked with a transaction! + + std::list resources; + database.GetAllPublicIds(resources, level); + + for (std::list::const_iterator + it = resources.begin(); it != resources.end(); it++) + { + // Locate the resource and one of its child instances + int64_t resource, instance; + ResourceType tmp; + + if (!database.LookupResource(resource, tmp, *it) || + tmp != level || + !FindOneChildInstance(instance, database, resource, level)) + { + throw OrthancException(ErrorCode_InternalError); + } + + // Get the DICOM file attached to some instances in the resource + FileInfo attachment; + if (!database.LookupAttachment(attachment, instance, FileContentType_Dicom)) + { + throw OrthancException(ErrorCode_InternalError); + } + + // Read and parse the content of the DICOM file + std::string content; + storageArea.Read(content, attachment.GetUuid(), FileContentType_Dicom); + + ParsedDicomFile dicom(content); + + // Update the tags of this resource + DicomMap dicomSummary; + dicom.Convert(dicomSummary); + + database.ClearMainDicomTags(resource); + + switch (level) + { + case ResourceType_Patient: + Toolbox::SetMainDicomTags(database, resource, ResourceType_Patient, dicomSummary, true); + break; + + case ResourceType_Study: + Toolbox::SetMainDicomTags(database, resource, ResourceType_Study, dicomSummary, true); + + // Duplicate the patient tags at the study level + Toolbox::SetMainDicomTags(database, resource, ResourceType_Patient, dicomSummary, false); + break; + + case ResourceType_Series: + Toolbox::SetMainDicomTags(database, resource, ResourceType_Series, dicomSummary, true); + break; + + case ResourceType_Instance: + Toolbox::SetMainDicomTags(database, resource, ResourceType_Instance, dicomSummary, true); + break; + + default: + throw OrthancException(ErrorCode_InternalError); + } } } } - - - void LogMissingRequiredTag(const DicomMap& summary) - { - std::string s, t; - - if (summary.HasTag(DICOM_TAG_PATIENT_ID)) - { - if (t.size() > 0) - t += ", "; - t += "PatientID=" + summary.GetValue(DICOM_TAG_PATIENT_ID).AsString(); - } - else - { - if (s.size() > 0) - s += ", "; - s += "PatientID"; - } - - if (summary.HasTag(DICOM_TAG_STUDY_INSTANCE_UID)) - { - if (t.size() > 0) - t += ", "; - t += "StudyInstanceUID=" + summary.GetValue(DICOM_TAG_STUDY_INSTANCE_UID).AsString(); - } - else - { - if (s.size() > 0) - s += ", "; - s += "StudyInstanceUID"; - } - - if (summary.HasTag(DICOM_TAG_SERIES_INSTANCE_UID)) - { - if (t.size() > 0) - t += ", "; - t += "SeriesInstanceUID=" + summary.GetValue(DICOM_TAG_SERIES_INSTANCE_UID).AsString(); - } - else - { - if (s.size() > 0) - s += ", "; - s += "SeriesInstanceUID"; - } - - if (summary.HasTag(DICOM_TAG_SOP_INSTANCE_UID)) - { - if (t.size() > 0) - t += ", "; - t += "SOPInstanceUID=" + summary.GetValue(DICOM_TAG_SOP_INSTANCE_UID).AsString(); - } - else - { - if (s.size() > 0) - s += ", "; - s += "SOPInstanceUID"; - } - - if (t.size() == 0) - { - LOG(ERROR) << "Store has failed because all the required tags (" << s << ") are missing (is it a DICOMDIR file?)"; - } - else - { - LOG(ERROR) << "Store has failed because required tags (" << s << ") are missing for the following instance: " << t; - } - } } diff -r 9e875db36aef -r de1413733c97 OrthancServer/ServerToolbox.h --- a/OrthancServer/ServerToolbox.h Wed Sep 30 14:04:53 2015 +0200 +++ b/OrthancServer/ServerToolbox.h Wed Sep 30 17:18:39 2015 +0200 @@ -33,13 +33,32 @@ #pragma once #include "../Core/DicomFormat/DicomMap.h" +#include "IDatabaseWrapper.h" #include namespace Orthanc { - void SimplifyTags(Json::Value& target, - const Json::Value& source); + namespace Toolbox + { + void SimplifyTags(Json::Value& target, + const Json::Value& source); + + void LogMissingRequiredTag(const DicomMap& summary); - void LogMissingRequiredTag(const DicomMap& summary); + void SetMainDicomTags(IDatabaseWrapper& database, + int64_t resource, + ResourceType level, + const DicomMap& dicomSummary, + bool includeIdentifiers); + + bool FindOneChildInstance(int64_t& result, + IDatabaseWrapper& database, + int64_t resource, + ResourceType type); + + void ReconstructMainDicomTags(IDatabaseWrapper& database, + IStorageArea& storageArea, + ResourceType level); + } } diff -r 9e875db36aef -r de1413733c97 OrthancServer/main.cpp --- a/OrthancServer/main.cpp Wed Sep 30 14:04:53 2015 +0200 +++ b/OrthancServer/main.cpp Wed Sep 30 17:18:39 2015 +0200 @@ -613,43 +613,114 @@ } + + +static bool ReconstructLevel(IDatabaseWrapper& database, + IStorageArea& storageArea, + bool allowDatabaseUpgrade, + ResourceType level) +{ + GlobalProperty property; + const char* plural = NULL; + + switch (level) + { + case ResourceType_Patient: + plural = "patients"; + property = GlobalProperty_ReconstructPatientsTags; + break; + + case ResourceType_Study: + plural = "studies"; + property = GlobalProperty_ReconstructStudiesTags; + break; + + case ResourceType_Series: + plural = "series"; + property = GlobalProperty_ReconstructSeriesTags; + break; + + case ResourceType_Instance: + plural = "instances"; + property = GlobalProperty_ReconstructInstancesTags; + break; + + default: + throw OrthancException(ErrorCode_InternalError); + } + + std::string tmp; + if (database.LookupGlobalProperty(tmp, property) && tmp != "0") + { + if (!allowDatabaseUpgrade) + { + LOG(ERROR) << "The main DICOM tags of all the " << plural + << " must be reconstructed: " + << "Please run Orthanc with the \"--upgrade\" command-line option"; + return false; + } + + std::auto_ptr transaction(database.StartTransaction()); + transaction->Begin(); + + LOG(WARNING) << "Upgrade: Reconstructing the main DICOM tags of all the " << plural << "..."; + Toolbox::ReconstructMainDicomTags(database, storageArea, level); + + database.SetGlobalProperty(property, "0"); + + transaction->Commit(); + } + + return true; +} + + static bool UpgradeDatabase(IDatabaseWrapper& database, IStorageArea& storageArea, bool allowDatabaseUpgrade) { - // Upgrade the database, if needed + // Upgrade the schema of the database, if needed unsigned int currentVersion = database.GetDatabaseVersion(); - if (currentVersion == ORTHANC_DATABASE_VERSION) + if (currentVersion != ORTHANC_DATABASE_VERSION) { - return true; + if (currentVersion > ORTHANC_DATABASE_VERSION) + { + LOG(ERROR) << "The version of the database schema (" << currentVersion + << ") is too recent for this version of Orthanc. Please upgrade Orthanc."; + return false; + } + + if (!allowDatabaseUpgrade) + { + LOG(ERROR) << "The database schema must be upgraded from version " + << currentVersion << " to " << ORTHANC_DATABASE_VERSION + << ": Please run Orthanc with the \"--upgrade\" command-line option"; + return false; + } + + LOG(WARNING) << "Upgrading the database from schema version " + << currentVersion << " to " << ORTHANC_DATABASE_VERSION; + database.Upgrade(ORTHANC_DATABASE_VERSION, storageArea); + + // Sanity check + currentVersion = database.GetDatabaseVersion(); + if (ORTHANC_DATABASE_VERSION != currentVersion) + { + LOG(ERROR) << "The database schema was not properly upgraded, it is still at version " << currentVersion; + throw OrthancException(ErrorCode_InternalError); + } } - if (currentVersion > ORTHANC_DATABASE_VERSION) + + // Reconstruct the main DICOM tags at each level, if needed + if (!ReconstructLevel(database, storageArea, allowDatabaseUpgrade, ResourceType_Patient) || + !ReconstructLevel(database, storageArea, allowDatabaseUpgrade, ResourceType_Study) || + !ReconstructLevel(database, storageArea, allowDatabaseUpgrade, ResourceType_Series) || + !ReconstructLevel(database, storageArea, allowDatabaseUpgrade, ResourceType_Instance)) { - LOG(ERROR) << "The version of the database (" << currentVersion - << ") is too recent for this version of Orthanc. Please upgrade Orthanc."; return false; } - if (!allowDatabaseUpgrade) - { - LOG(ERROR) << "The database must be upgraded from version " - << currentVersion << " to " << ORTHANC_DATABASE_VERSION - << ": Please run Orthanc with the \"--upgrade\" command-line option"; - return false; - } - - LOG(WARNING) << "Upgrading the database from version " - << currentVersion << " to " << ORTHANC_DATABASE_VERSION; - database.Upgrade(ORTHANC_DATABASE_VERSION, storageArea); - - // Sanity check - currentVersion = database.GetDatabaseVersion(); - if (ORTHANC_DATABASE_VERSION != currentVersion) - { - LOG(ERROR) << "The database was not properly updated, it is still at version " << currentVersion; - throw OrthancException(ErrorCode_InternalError); - } return true; } diff -r 9e875db36aef -r de1413733c97 Plugins/Engine/OrthancPluginDatabase.cpp --- a/Plugins/Engine/OrthancPluginDatabase.cpp Wed Sep 30 14:04:53 2015 +0200 +++ b/Plugins/Engine/OrthancPluginDatabase.cpp Wed Sep 30 17:18:39 2015 +0200 @@ -985,6 +985,24 @@ } + void OrthancPluginDatabase::ClearMainDicomTags(int64_t id) + { + if (extensions_.clearMainDicomTags != NULL) + { + LOG(ERROR) << "Your custom index plugin does not implement the ClearMainDicomTags() extension"; + throw OrthancException(ErrorCode_DatabasePlugin); + } + + OrthancPluginErrorCode error = extensions_.clearMainDicomTags(payload_, id); + + if (error != OrthancPluginErrorCode_Success) + { + errorDictionary_.LogError(error, true); + throw OrthancException(static_cast(error)); + } + } + + void OrthancPluginDatabase::SetMainDicomTag(int64_t id, const DicomTag& tag, const std::string& value) diff -r 9e875db36aef -r de1413733c97 Plugins/Engine/OrthancPluginDatabase.h --- a/Plugins/Engine/OrthancPluginDatabase.h Wed Sep 30 14:04:53 2015 +0200 +++ b/Plugins/Engine/OrthancPluginDatabase.h Wed Sep 30 17:18:39 2015 +0200 @@ -217,6 +217,8 @@ virtual void SetGlobalProperty(GlobalProperty property, const std::string& value); + virtual void ClearMainDicomTags(int64_t id); + virtual void SetMainDicomTag(int64_t id, const DicomTag& tag, const std::string& value); diff -r 9e875db36aef -r de1413733c97 Plugins/Engine/OrthancPlugins.cpp --- a/Plugins/Engine/OrthancPlugins.cpp Wed Sep 30 14:04:53 2015 +0200 +++ b/Plugins/Engine/OrthancPlugins.cpp Wed Sep 30 17:18:39 2015 +0200 @@ -1006,7 +1006,7 @@ else { Json::Value simplified; - SimplifyTags(simplified, instance.GetJson()); + Toolbox::SimplifyTags(simplified, instance.GetJson()); s = writer.write(simplified); } diff -r 9e875db36aef -r de1413733c97 Plugins/Include/orthanc/OrthancCDatabasePlugin.h --- a/Plugins/Include/orthanc/OrthancCDatabasePlugin.h Wed Sep 30 14:04:53 2015 +0200 +++ b/Plugins/Include/orthanc/OrthancCDatabasePlugin.h Wed Sep 30 17:18:39 2015 +0200 @@ -655,7 +655,12 @@ void* payload, uint32_t targetVersion, OrthancPluginStorageArea* storageArea); - } OrthancPluginDatabaseExtensions; + + OrthancPluginErrorCode (*clearMainDicomTags) ( + /* inputs */ + void* payload, + int64_t id); + } OrthancPluginDatabaseExtensions; /*(payload); + + try + { + backend->ClearMainDicomTags(internalId); + return OrthancPluginErrorCode_Success; + } + catch (std::runtime_error& e) + { + LogError(backend, e); + return OrthancPluginErrorCode_DatabasePlugin; + } + catch (DatabaseException& e) + { + return e.GetErrorCode(); + } + } + + public: /** * Register a custom database back-end written in C++. @@ -1847,6 +1871,7 @@ extensions.getAllPublicIdsWithLimit = GetAllPublicIdsWithLimit; extensions.getDatabaseVersion = GetDatabaseVersion; extensions.upgradeDatabase = UpgradeDatabase; + extensions.clearMainDicomTags = ClearMainDicomTags; OrthancPluginDatabaseContext* database = OrthancPluginRegisterDatabaseBackendV2(context, ¶ms, &extensions, &backend); if (!context) diff -r 9e875db36aef -r de1413733c97 Resources/Configuration.json --- a/Resources/Configuration.json Wed Sep 30 14:04:53 2015 +0200 +++ b/Resources/Configuration.json Wed Sep 30 17:18:39 2015 +0200 @@ -243,7 +243,8 @@ "KeepAlive" : false, // If this option is set to "false", Orthanc will run in index-only - // mode. The DICOM files will not be stored on the drive. + // mode. The DICOM files will not be stored on the drive. Note that + // this option might prevent the upgrade to newer versions of Orthanc. "StoreDicom" : true, // DICOM associations are kept open as long as new DICOM commands