changeset 344:cd6749e53a03

anonymization from orthanc explorer
author Sebastien Jodogne <s.jodogne@gmail.com>
date Mon, 21 Jan 2013 13:23:26 +0100
parents b1edbd636cb8
children 795ce0fa8e8a
files OrthancExplorer/explorer.html OrthancExplorer/explorer.js OrthancServer/OrthancRestApi.cpp
diffstat 3 files changed, 192 insertions(+), 108 deletions(-) [+]
line wrap: on
line diff
--- a/OrthancExplorer/explorer.html	Fri Jan 18 10:25:55 2013 +0100
+++ b/OrthancExplorer/explorer.html	Mon Jan 21 13:23:26 2013 +0100
@@ -123,6 +123,7 @@
               <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>
+                <a href="#" data-role="button" data-icon="star" id="study-anonymize">Anonymize</a>
               </p>
             </div>
           </div>
@@ -159,6 +160,7 @@
                 <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>
+                <a href="#" data-role="button" data-icon="star" id="series-anonymize">Anonymize</a>
               </p>
             </div>
           </div>
--- a/OrthancExplorer/explorer.js	Fri Jan 18 10:25:55 2013 +0100
+++ b/OrthancExplorer/explorer.js	Mon Jan 21 13:23:26 2013 +0100
@@ -144,6 +144,7 @@
     url: '../' + type + '/' + uuid,
     dataType: 'json',
     async: false,
+    cache: false,
     success: function(s) {
       callback(s);
     }
@@ -159,6 +160,7 @@
       url: '../' + type,
       dataType: 'json',
       async: false,
+      cache: false,
       success: function(s) {
         uuids = s;
       }
@@ -171,6 +173,7 @@
       url: '../' + type + '/' + uuid,
       dataType: 'json',
       async: true,
+      cache: false,
       success: function(s) {
         resources.push(s);
       }
@@ -324,6 +327,7 @@
     url: '../system',
     dataType: 'json',
     async: false,
+    cache: false,
     success: function(s) {
       if (s.Name != "") {
         $('.orthanc-name').html('<a class="ui-link" href="explorer.html">' + s.Name + '</a> &raquo; ');
@@ -385,6 +389,7 @@
           type: 'GET',
           dataType: 'text',
           async: false,
+          cache: false,
           success: function (s) {
             var v = (s == '1') ? 'on' : 'off';
             $('#protection').val(v).slider('refresh');
@@ -542,6 +547,7 @@
 
             $.ajax({
               url: '../instances/' + instance.ID + '/tags',
+              cache: false,
               dataType: 'json',
               success: function(s) {
                 $('#dicom-tree').tree('loadData', ConvertForTree(s));
@@ -707,6 +713,7 @@
     type: 'GET',
     dataType: 'json',
     async: false,
+    cache: false,
     success: function(modalities) {
       var clickedModality = '';
       var items = $('<ul>')
@@ -808,3 +815,54 @@
     async: false
   });
 });
+
+
+
+function OpenAnonymizeResourceDialog(path, title)
+{
+  $(document).simpledialog2({ 
+    mode: 'button',
+    animate: false,
+    headerText: title,
+    headerClose: true,
+    width: '500px',
+    buttons : {
+      'OK': {
+        click: function () { 
+          $.ajax({
+            url: path + '/anonymize',
+            type: 'POST',
+            data: '{}',
+            dataType: 'json',
+            async: false,
+            cache: false,
+            success: function(s) {
+              $.mobile.changePage('#patient?uuid=' + s.PatientID);
+            }
+          });
+        },
+        icon: "delete",
+        theme: "c"
+      },
+      'Cancel': {
+        click: function () { 
+        }
+      }
+    }
+  });
+}
+
+$('#instance-anonymize').live('click', function() {
+  OpenAnonymizeResourceDialog('../instances/' + $.mobile.pageData.uuid,
+                              'Anonymize this instance?');
+});
+
+$('#study-anonymize').live('click', function() {
+  OpenAnonymizeResourceDialog('../studies/' + $.mobile.pageData.uuid,
+                              'Anonymize this study?');
+});
+
+$('#series-anonymize').live('click', function() {
+  OpenAnonymizeResourceDialog('../series/' + $.mobile.pageData.uuid,
+                              'Anonymize this series?');
+});
--- a/OrthancServer/OrthancRestApi.cpp	Fri Jan 18 10:25:55 2013 +0100
+++ b/OrthancServer/OrthancRestApi.cpp	Mon Jan 21 13:23:26 2013 +0100
@@ -1166,59 +1166,70 @@
                                       ChangeType changeType,
                                       RestApi::PostCall& call)
   {
-    RETRIEVE_CONTEXT(call);
+    Json::Value result = Json::objectValue;
     
-    boost::mutex::scoped_lock lock(cacheMutex_);
-
-    typedef std::list<std::string> Instances;
-    Instances instances;
-    std::string id = call.GetUriComponent("id", "");
-    context.GetIndex().GetChildInstances(instances, id);
-
-    if (instances.size() == 0)
     {
-      return;
-    }
+      boost::mutex::scoped_lock lock(cacheMutex_);
+      RETRIEVE_CONTEXT(call);
 
-    replacements[DICOM_TAG_SERIES_INSTANCE_UID] = FromDcmtkBridge::GenerateUniqueIdentifier(DicomRootLevel_Series);
+      typedef std::list<std::string> Instances;
+      Instances instances;
+      std::string id = call.GetUriComponent("id", "");
+      context.GetIndex().GetChildInstances(instances, id);
 
-    std::string newSeriesId;
-    for (Instances::const_iterator it = instances.begin(); 
-         it != instances.end(); it++)
-    {
-      LOG(INFO) << "Modifying instance " << *it;
-      ParsedDicomFile& original = context.GetDicomFile(*it);
-      std::auto_ptr<ParsedDicomFile> modified(original.Clone());
-      ReplaceInstanceInternal(*modified, removals, replacements, DicomReplaceMode_InsertIfAbsent, removePrivateTags);
-
-      std::string modifiedInstance;
-      if (context.Store(modifiedInstance, modified->GetDicom()) != StoreStatus_Success)
+      if (instances.size() == 0)
       {
-        LOG(ERROR) << "Error while storing a modified instance " << *it;
         return;
       }
 
-      DicomInstanceHasher modifiedHasher = modified->GetHasher();
-      DicomInstanceHasher originalHasher = original.GetHasher();
+      replacements[DICOM_TAG_SERIES_INSTANCE_UID] = FromDcmtkBridge::GenerateUniqueIdentifier(DicomRootLevel_Series);
+
+      std::string newSeriesId, newPatientId;
+      for (Instances::const_iterator it = instances.begin(); 
+           it != instances.end(); it++)
+      {
+        LOG(INFO) << "Modifying instance " << *it;
+        ParsedDicomFile& original = context.GetDicomFile(*it);
+        std::auto_ptr<ParsedDicomFile> modified(original.Clone());
+        ReplaceInstanceInternal(*modified, removals, replacements, DicomReplaceMode_InsertIfAbsent, removePrivateTags);
+
+        std::string modifiedInstance;
+        if (context.Store(modifiedInstance, modified->GetDicom()) != StoreStatus_Success)
+        {
+          LOG(ERROR) << "Error while storing a modified instance " << *it;
+          return;
+        }
 
-      if (newSeriesId.size() == 0)
-      {
-        assert(id == originalHasher.HashSeries());
-        newSeriesId = modifiedHasher.HashSeries();
-        context.GetIndex().SetMetadata(newSeriesId, metadataType, id);
+        DicomInstanceHasher modifiedHasher = modified->GetHasher();
+        DicomInstanceHasher originalHasher = original.GetHasher();
+
+        if (newSeriesId.size() == 0)
+        {
+          assert(id == originalHasher.HashSeries());
+          newSeriesId = modifiedHasher.HashSeries();
+          context.GetIndex().SetMetadata(newSeriesId, metadataType, id);
+        }
+
+        if (newPatientId.size() == 0)
+        {
+          newPatientId = modifiedHasher.HashPatient();
+        }
+
+        assert(*it == originalHasher.HashInstance());
+        assert(modifiedInstance == modifiedHasher.HashInstance());
+        context.GetIndex().SetMetadata(modifiedInstance, metadataType, *it);
       }
 
-      assert(*it == originalHasher.HashInstance());
-      assert(modifiedInstance == modifiedHasher.HashInstance());
-      context.GetIndex().SetMetadata(modifiedInstance, metadataType, *it);
+      context.GetIndex().LogChange(changeType, newSeriesId);
+
+      assert(newSeriesId.size() != 0);
+      result["Type"] = ToString(ResourceType_Series);
+      result["ID"] = newSeriesId;
+      result["Path"] = GetBasePath(ResourceType_Series, newSeriesId);
+      result["PatientID"] = newPatientId;
     }
 
-    context.GetIndex().LogChange(changeType, newSeriesId);
-
-    assert(newSeriesId.size() != 0);
-    Json::Value result = Json::objectValue;
-    result["ID"] = newSeriesId;
-    result["Path"] = GetBasePath(ResourceType_Series, newSeriesId);
+    // Do not answer before the lock has been released
     call.GetOutput().AnswerJson(result);
   }
 
@@ -1230,88 +1241,101 @@
                                      ChangeType changeType,
                                      RestApi::PostCall& call)
   {
-    RETRIEVE_CONTEXT(call);
-    boost::mutex::scoped_lock lock(cacheMutex_);
-
-    typedef std::list<std::string> Instances;
-    typedef std::map<std::string, std::string> SeriesUidMap;
+    Json::Value result = Json::objectValue;
 
-    Instances instances;
-    std::string id = call.GetUriComponent("id", "");
-    context.GetIndex().GetChildInstances(instances, id);
-
-    if (instances.size() == 0)
     {
-      return;
-    }
-
-    std::string newStudyId;
-    replacements[DICOM_TAG_STUDY_INSTANCE_UID] = FromDcmtkBridge::GenerateUniqueIdentifier(DicomRootLevel_Study);
-
-    SeriesUidMap seriesUidMap;
-    for (Instances::const_iterator it = instances.begin(); 
-         it != instances.end(); it++)
-    {
-      LOG(INFO) << "Modifying instance " << *it;
-      ParsedDicomFile& original = context.GetDicomFile(*it);
+      boost::mutex::scoped_lock lock(cacheMutex_);
+      RETRIEVE_CONTEXT(call);
 
-      std::string seriesUid;
-      if (!original.GetTagValue(seriesUid, DICOM_TAG_SERIES_INSTANCE_UID))
-      {
-        throw OrthancException(ErrorCode_InternalError);
-      }
+      typedef std::list<std::string> Instances;
+      typedef std::map<std::string, std::string> SeriesUidMap;
 
-      bool isNewSeries;
-      SeriesUidMap::const_iterator it2 = seriesUidMap.find(seriesUid);
-      if (it2 == seriesUidMap.end())
+      Instances instances;
+      std::string id = call.GetUriComponent("id", "");
+      context.GetIndex().GetChildInstances(instances, id);
+
+      if (instances.size() == 0)
       {
-        std::string newSeriesUid = FromDcmtkBridge::GenerateUniqueIdentifier(DicomRootLevel_Series);
-        seriesUidMap[seriesUid] = newSeriesUid;
-        replacements[DICOM_TAG_SERIES_INSTANCE_UID] = newSeriesUid;
-        isNewSeries = true;
-      }
-      else
-      {
-        replacements[DICOM_TAG_SERIES_INSTANCE_UID] = it2->second;
-        isNewSeries = false;
-      }
-
-      std::auto_ptr<ParsedDicomFile> modified(original.Clone());
-      ReplaceInstanceInternal(*modified, removals, replacements, DicomReplaceMode_InsertIfAbsent, removePrivateTags);
-
-      std::string modifiedInstance;
-      if (context.Store(modifiedInstance, modified->GetDicom()) != StoreStatus_Success)
-      {
-        LOG(ERROR) << "Error while storing a modified instance " << *it;
         return;
       }
 
-      DicomInstanceHasher modifiedHasher = modified->GetHasher();
-      DicomInstanceHasher originalHasher = original.GetHasher();
+      std::string newPatientId;
+      std::string newStudyId;
+      replacements[DICOM_TAG_STUDY_INSTANCE_UID] = FromDcmtkBridge::GenerateUniqueIdentifier(DicomRootLevel_Study);
+
+      SeriesUidMap seriesUidMap;
+      for (Instances::const_iterator it = instances.begin(); 
+           it != instances.end(); it++)
+      {
+        LOG(INFO) << "Modifying instance " << *it;
+        ParsedDicomFile& original = context.GetDicomFile(*it);
+
+        std::string seriesUid;
+        if (!original.GetTagValue(seriesUid, DICOM_TAG_SERIES_INSTANCE_UID))
+        {
+          throw OrthancException(ErrorCode_InternalError);
+        }
+
+        bool isNewSeries;
+        SeriesUidMap::const_iterator it2 = seriesUidMap.find(seriesUid);
+        if (it2 == seriesUidMap.end())
+        {
+          std::string newSeriesUid = FromDcmtkBridge::GenerateUniqueIdentifier(DicomRootLevel_Series);
+          seriesUidMap[seriesUid] = newSeriesUid;
+          replacements[DICOM_TAG_SERIES_INSTANCE_UID] = newSeriesUid;
+          isNewSeries = true;
+        }
+        else
+        {
+          replacements[DICOM_TAG_SERIES_INSTANCE_UID] = it2->second;
+          isNewSeries = false;
+        }
 
-      if (isNewSeries)
-      {
-        context.GetIndex().SetMetadata
-          (modifiedHasher.HashSeries(), metadataType, originalHasher.HashSeries());
+        std::auto_ptr<ParsedDicomFile> modified(original.Clone());
+        ReplaceInstanceInternal(*modified, removals, replacements, DicomReplaceMode_InsertIfAbsent, removePrivateTags);
+
+        std::string modifiedInstance;
+        if (context.Store(modifiedInstance, modified->GetDicom()) != StoreStatus_Success)
+        {
+          LOG(ERROR) << "Error while storing a modified instance " << *it;
+          return;
+        }
+
+        DicomInstanceHasher modifiedHasher = modified->GetHasher();
+        DicomInstanceHasher originalHasher = original.GetHasher();
+
+        if (isNewSeries)
+        {
+          context.GetIndex().SetMetadata
+            (modifiedHasher.HashSeries(), metadataType, originalHasher.HashSeries());
+        }
+
+        if (newStudyId.size() == 0)
+        {
+          newStudyId = modifiedHasher.HashStudy();
+          context.GetIndex().SetMetadata(newStudyId, metadataType, originalHasher.HashStudy());
+        }
+
+        if (newPatientId.size() == 0)
+        {
+          newPatientId = modifiedHasher.HashPatient();
+        }
+
+        assert(*it == originalHasher.HashInstance());
+        assert(modifiedInstance == modifiedHasher.HashInstance());
+        context.GetIndex().SetMetadata(modifiedInstance, metadataType, *it);
       }
 
-      if (newStudyId.size() == 0)
-      {
-        newStudyId = modifiedHasher.HashStudy();
-        context.GetIndex().SetMetadata(newStudyId, metadataType, originalHasher.HashStudy());
-      }
+      context.GetIndex().LogChange(changeType, newStudyId);
 
-      assert(*it == originalHasher.HashInstance());
-      assert(modifiedInstance == modifiedHasher.HashInstance());
-      context.GetIndex().SetMetadata(modifiedInstance, metadataType, *it);
+      assert(newStudyId.size() != 0);
+      result["Type"] = ToString(ResourceType_Study);
+      result["ID"] = newStudyId;
+      result["Path"] = GetBasePath(ResourceType_Study, newStudyId);
+      result["PatientID"] = newPatientId;
     }
 
-    context.GetIndex().LogChange(changeType, newStudyId);
-
-    assert(newStudyId.size() != 0);
-    Json::Value result = Json::objectValue;
-    result["ID"] = newStudyId;
-    result["Path"] = GetBasePath(ResourceType_Study, newStudyId);
+    // Do not answer before the lock has been released
     call.GetOutput().AnswerJson(result);
   }