changeset 440:23e5b35e3c5c

statistics for patient/studies/series/instances
author Sebastien Jodogne <s.jodogne@gmail.com>
date Wed, 15 May 2013 17:44:15 +0200
parents 081a44d5110b
children 9d876ea52df4 40deb6525337
files NEWS OrthancServer/DatabaseWrapper.cpp OrthancServer/DatabaseWrapper.h OrthancServer/OrthancRestApi.cpp OrthancServer/ServerIndex.cpp OrthancServer/ServerIndex.h
diffstat 6 files changed, 142 insertions(+), 6 deletions(-) [+]
line wrap: on
line diff
--- a/NEWS	Wed May 15 17:10:52 2013 +0200
+++ b/NEWS	Wed May 15 17:44:15 2013 +0200
@@ -13,6 +13,7 @@
 Other
 -----
 
+* Statistics about patients, studies, series and instances
 * Fixes for Red Hat and Debian packaging
 * Fixes for boost::thread, as reported by Cyril Paulus
 
--- a/OrthancServer/DatabaseWrapper.cpp	Wed May 15 17:10:52 2013 +0200
+++ b/OrthancServer/DatabaseWrapper.cpp	Wed May 15 17:44:15 2013 +0200
@@ -438,6 +438,21 @@
     s.Run();
   }
 
+  void DatabaseWrapper::ListAvailableAttachments(std::list<FileContentType>& result,
+                                                 int64_t id)
+  {
+    result.clear();
+
+    SQLite::Statement s(db_, SQLITE_FROM_HERE, 
+                        "SELECT fileType FROM AttachedFiles WHERE id=?");
+    s.BindInt(0, id);
+
+    while (s.Step())
+    {
+      result.push_back(static_cast<FileContentType>(s.ColumnInt(0)));
+    }
+  }
+
   bool DatabaseWrapper::LookupAttachment(FileInfo& attachment,
                                          int64_t id,
                                          FileContentType contentType)
--- a/OrthancServer/DatabaseWrapper.h	Wed May 15 17:10:52 2013 +0200
+++ b/OrthancServer/DatabaseWrapper.h	Wed May 15 17:44:15 2013 +0200
@@ -129,6 +129,9 @@
     void AddAttachment(int64_t id,
                        const FileInfo& attachment);
 
+    void ListAvailableAttachments(std::list<FileContentType>& result,
+                                  int64_t id);
+
     bool LookupAttachment(FileInfo& attachment,
                           int64_t id,
                           FileContentType contentType);
--- a/OrthancServer/OrthancRestApi.cpp	Wed May 15 17:10:52 2013 +0200
+++ b/OrthancServer/OrthancRestApi.cpp	Wed May 15 17:44:15 2013 +0200
@@ -1573,6 +1573,16 @@
   }
 
 
+  static void GetResourceStatistics(RestApi::GetCall& call)
+  {
+    RETRIEVE_CONTEXT(call);
+    std::string publicId = call.GetUriComponent("id", "");
+    Json::Value result;
+    context.GetIndex().GetStatistics(result, publicId);
+    call.GetOutput().AnswerJson(result);
+  }
+
+
 
   // Registration of the various REST handlers --------------------------------
 
@@ -1608,6 +1618,11 @@
     Register("/studies/{id}/archive", GetArchive<ResourceType_Study>);
     Register("/series/{id}/archive", GetArchive<ResourceType_Series>);
 
+    Register("/instances/{id}/statistics", GetResourceStatistics);
+    Register("/patients/{id}/statistics", GetResourceStatistics);
+    Register("/studies/{id}/statistics", GetResourceStatistics);
+    Register("/series/{id}/statistics", GetResourceStatistics);
+
     Register("/instances/{id}/metadata", ListMetadata);
     Register("/instances/{id}/metadata/{name}", DeleteMetadata);
     Register("/instances/{id}/metadata/{name}", GetMetadata);
--- a/OrthancServer/ServerIndex.cpp	Wed May 15 17:10:52 2013 +0200
+++ b/OrthancServer/ServerIndex.cpp	Wed May 15 17:44:15 2013 +0200
@@ -48,6 +48,8 @@
 #include <stdio.h>
 #include <glog/logging.h>
 
+static const uint64_t MEGA_BYTES = 1024 * 1024;
+
 namespace Orthanc
 {
   namespace Internals
@@ -515,18 +517,16 @@
 
   void ServerIndex::ComputeStatistics(Json::Value& target)
   {
-    static const uint64_t MB = 1024 * 1024;
-
     boost::mutex::scoped_lock lock(mutex_);
     target = Json::objectValue;
 
     uint64_t cs = currentStorageSize_;
     assert(cs == db_->GetTotalCompressedSize());
     uint64_t us = db_->GetTotalUncompressedSize();
-    target["TotalDiskSpace"] = boost::lexical_cast<std::string>(cs);
+    target["TotalDiskSize"] = boost::lexical_cast<std::string>(cs);
     target["TotalUncompressedSize"] = boost::lexical_cast<std::string>(us);
-    target["TotalDiskSpaceMB"] = boost::lexical_cast<unsigned int>(cs / MB);
-    target["TotalUncompressedSizeMB"] = boost::lexical_cast<unsigned int>(us / MB);
+    target["TotalDiskSizeMB"] = boost::lexical_cast<unsigned int>(cs / MEGA_BYTES);
+    target["TotalUncompressedSizeMB"] = boost::lexical_cast<unsigned int>(us / MEGA_BYTES);
 
     target["CountPatients"] = static_cast<unsigned int>(db_->GetResourceCount(ResourceType_Patient));
     target["CountStudies"] = static_cast<unsigned int>(db_->GetResourceCount(ResourceType_Study));
@@ -1001,7 +1001,7 @@
     }
     else
     {
-      LOG(WARNING) << "At most " << (size / (1024 * 1024)) << "MB will be used for the storage area";
+      LOG(WARNING) << "At most " << (size / MEGA_BYTES) << "MB will be used for the storage area";
     }
 
     StandaloneRecycling();
@@ -1244,4 +1244,103 @@
     boost::mutex::scoped_lock lock(mutex_);
     db_->ClearTable("ExportedResources");
   }
+
+
+  void ServerIndex::GetStatistics(Json::Value& target,
+                                  const std::string& publicId)
+  {
+    boost::mutex::scoped_lock lock(mutex_);
+
+    ResourceType type;
+    int64_t top;
+    if (!db_->LookupResource(publicId, top, type))
+    {
+      throw OrthancException(ErrorCode_UnknownResource);
+    }
+
+    std::stack<int64_t> toExplore;
+    toExplore.push(top);
+
+    int countInstances = 0;
+    int countSeries = 0;
+    int countStudies = 0;
+    uint64_t compressedSize = 0;
+    uint64_t uncompressedSize = 0;
+
+    while (!toExplore.empty())
+    {
+      // Get the internal ID of the current resource
+      int64_t resource = toExplore.top();
+      toExplore.pop();
+
+      ResourceType thisType = db_->GetResourceType(resource);
+
+      if (thisType == ResourceType_Instance)
+      {
+        std::list<FileContentType> f;
+        db_->ListAvailableAttachments(f, resource);
+
+        for (std::list<FileContentType>::const_iterator
+               it = f.begin(); it != f.end(); it++)
+        {
+          FileInfo attachment;
+          if (db_->LookupAttachment(attachment, resource, *it))
+          {
+            compressedSize += attachment.GetCompressedSize();
+            uncompressedSize += attachment.GetUncompressedSize();
+          }
+        }
+
+        countInstances++;
+      }
+      else
+      {
+        switch (thisType)
+        {
+          case ResourceType_Study:
+            countStudies++;
+            break;
+
+          case ResourceType_Series:
+            countSeries++;
+            break;
+
+          default:
+            break;
+        }
+
+        // Tag all the children of this resource as to be explored
+        std::list<int64_t> tmp;
+        db_->GetChildrenInternalId(tmp, resource);
+        for (std::list<int64_t>::const_iterator 
+               it = tmp.begin(); it != tmp.end(); it++)
+        {
+          toExplore.push(*it);
+        }
+      }
+    }
+
+    target = Json::objectValue;
+    target["DiskSize"] = boost::lexical_cast<std::string>(compressedSize);
+    target["DiskSizeMB"] = boost::lexical_cast<unsigned int>(compressedSize / MEGA_BYTES);
+    target["UncompressedSize"] = boost::lexical_cast<std::string>(uncompressedSize);
+    target["UncompressedSizeMB"] = boost::lexical_cast<unsigned int>(uncompressedSize / MEGA_BYTES);
+
+    switch (type)
+    {
+      // Do NOT add "break" below this point!
+      case ResourceType_Patient:
+        target["CountStudies"] = countStudies;
+
+      case ResourceType_Study:
+        target["CountSeries"] = countSeries;
+
+      case ResourceType_Series:
+        target["CountInstances"] = countInstances;
+
+      case ResourceType_Instance:
+      default:
+        break;
+    }
+  }
 }
--- a/OrthancServer/ServerIndex.h	Wed May 15 17:10:52 2013 +0200
+++ b/OrthancServer/ServerIndex.h	Wed May 15 17:44:15 2013 +0200
@@ -171,5 +171,8 @@
     void DeleteChanges();
 
     void DeleteExportedResources();
+
+    void GetStatistics(Json::Value& target,
+                       const std::string& publicId);
   };
 }