Przeglądaj źródła

Continued refactoring of the upload functions:

Adapted upload() function of xnatSession
Added saveImpl() function to xnatObject which should be overwritten by subclasses if needed
Added saveImpl() to xnatFile and xnatResource
Adapted test
Updated qRestAPI revision so that file upload is supported via qRestAPI now
Andreas Fetzer 10 lat temu
rodzic
commit
57bf105fa0

+ 1 - 1
CMakeExternals/qRestAPI.cmake

@@ -24,7 +24,7 @@ endif()
 
 if(NOT DEFINED ${proj}_DIR)
 
-  set(revision_tag "5f3a03b15d")
+  set(revision_tag "4293694a89")
   if(${proj}_REVISION_TAG)
     set(revision_tag ${${proj}_REVISION_TAG})
   endif()

+ 1 - 1
Libs/XNAT/Core/Testing/ctkXnatSessionTest.cpp

@@ -223,7 +223,7 @@ void ctkXnatSessionTestCase::testCreateProject()
   bool exists = d->Session->exists(project);
   QVERIFY(!exists);
 
-  d->Session->save(project);
+  project->save();
 
   exists = d->Session->exists(project);
   QVERIFY(exists);

+ 58 - 5
Libs/XNAT/Core/ctkXnatFile.cpp

@@ -21,9 +21,12 @@
 
 #include "ctkXnatFile.h"
 
+#include "ctkXnatException.h"
 #include "ctkXnatObjectPrivate.h"
 #include "ctkXnatSession.h"
 
+#include <QFile>
+
 const QString ctkXnatFile::FILE_NAME = "Name";
 const QString ctkXnatFile::FILE_TAGS = "file_tags";
 const QString ctkXnatFile::FILE_FORMAT = "file_format";
@@ -41,10 +44,9 @@ public:
 
   void reset()
   {
-//    uri.clear();
   }
 
-//  QString uri;
+  QString localFilePath;
 };
 
 
@@ -77,6 +79,7 @@ void ctkXnatFile::setFileFormat(const QString &fileFormat)
   this->setProperty(FILE_FORMAT, fileFormat);
 }
 
+//----------------------------------------------------------------------------
 QString ctkXnatFile::fileFormat() const
 {
   return this->property(FILE_FORMAT);
@@ -88,6 +91,7 @@ void ctkXnatFile::setFileContent(const QString &fileContent)
   this->setProperty(FILE_CONTENT, fileContent);
 }
 
+//----------------------------------------------------------------------------
 QString ctkXnatFile::fileContent() const
 {
   return this->property(FILE_CONTENT);
@@ -99,20 +103,30 @@ void ctkXnatFile::setFileTags(const QString &fileTags)
   this->setProperty(FILE_TAGS, fileTags);
 }
 
+//----------------------------------------------------------------------------
 QString ctkXnatFile::fileTags() const
 {
   return this->property(FILE_TAGS);
 }
 
 //----------------------------------------------------------------------------
-QString ctkXnatFile::resourceUri() const
+void ctkXnatFile::setLocalFilePath(const QString &filePath)
 {
-  return QString("%1/files/%2").arg(parent()->resourceUri(), this->name());
+  Q_D(ctkXnatFile);
+  d->localFilePath = filePath;
+}
+
+//----------------------------------------------------------------------------
+QString ctkXnatFile::localFilePath() const
+{
+  Q_D(const ctkXnatFile);
+  return d->localFilePath;
 }
 
 //----------------------------------------------------------------------------
-void ctkXnatFile::upload(const QString& /*filename*/)
+QString ctkXnatFile::resourceUri() const
 {
+  return QString("%1/files/%2").arg(parent()->resourceUri(), this->name());
 }
 
 //----------------------------------------------------------------------------
@@ -132,3 +146,42 @@ void ctkXnatFile::downloadImpl(const QString& filename)
   QString query = this->resourceUri();
   this->session()->download(filename, query);
 }
+
+//----------------------------------------------------------------------------
+void ctkXnatFile::saveImpl()
+{
+  QString query = this->resourceUri();
+  QString filename = this->localFilePath();
+
+  QFile file(filename);
+
+  if (!file.exists())
+  {
+    QString msg = "Error uploading file! ";
+    msg.append(QString("File \"%1\" does not exist!").arg(filename));
+    throw ctkXnatException(msg);
+  }
+
+  // Creating the update query
+  query.append(QString("?%1=%2").arg("xsi:type", this->schemaType()));
+  const QMap<QString, QString>& properties = this->properties();
+  QMapIterator<QString, QString> itProperties(properties);
+  while (itProperties.hasNext())
+  {
+    itProperties.next();
+
+    // Do not append these file specific properties since they require a slightly
+    // different key for uploading a file (e.g. instead of "file_format" only "format")
+    if (itProperties.key() == FILE_TAGS || itProperties.key() == FILE_FORMAT ||
+        itProperties.key() == FILE_CONTENT)
+      continue;
+
+    query.append(QString("&%1=%2").arg(itProperties.key(), itProperties.value()));
+  }
+  query.append(QString("&%1=%2").arg("format", this->fileFormat()));
+  query.append(QString("&%1=%2").arg("content", this->fileContent()));
+  query.append(QString("&%1=%2").arg("tags", this->fileTags()));
+  query.append(QString("&%1=%2").arg("inbody", "true"));
+
+  this->session()->upload(filename, query);
+}

+ 9 - 1
Libs/XNAT/Core/ctkXnatFile.h

@@ -56,7 +56,8 @@ public:
   void setFileContent(const QString& fileContent);
   QString fileContent() const;
 
-  void upload(const QString& filename);
+  void setLocalFilePath(const QString& filepath);
+  QString localFilePath() const;
 
   void reset();
 
@@ -71,6 +72,13 @@ private:
 
   virtual void downloadImpl(const QString&);
 
+  /**
+    * @brief Uploads the file to the server
+    * Before calling save() the localFilePath has to be set
+    * @throws ctkXnatException it the specified file does not exists
+    */
+  virtual void saveImpl();
+
   Q_DECLARE_PRIVATE(ctkXnatFile)
 };
 

+ 27 - 12
Libs/XNAT/Core/ctkXnatObject.cpp

@@ -300,8 +300,12 @@ void ctkXnatObject::download(const QString& filename)
 }
 
 //----------------------------------------------------------------------------
-void ctkXnatObject::upload(const QString& /*zipFilename*/)
+void ctkXnatObject::save()
 {
+  this->saveImpl();
+}
+
+//----------------------------------------------------------------------------
 }
 
 //----------------------------------------------------------------------------
@@ -311,18 +315,26 @@ bool ctkXnatObject::exists() const
 }
 
 //----------------------------------------------------------------------------
-void ctkXnatObject::commit ()
+void ctkXnatObject::saveImpl()
 {
   Q_D(ctkXnatObject);
   QString query = this->resourceUri();
-  QDateTime remoteModTime = this->lastModifiedTimeOnServer();
-  // If the object has been modified on the server, perform an update
-  if (d->lastModifiedTime < remoteModTime)
+
+  // If there is already a valid last-modification-time, otherwise the
+  // object is not yet on the server!
+  QDateTime remoteModTime;
+  if (d->lastModifiedTime.isValid())
   {
-    qDebug()<<"Object maybe overwritten on server!";
-    // TODO update from server, since modification time is not really supported
-    // by xnat right now this is not of high priority
-    // something like this->updateImpl
+    // TODO Overwrite this for e.g. project and subject which already support modification time!
+    remoteModTime = this->lastModifiedTimeOnServer();
+    // If the object has been modified on the server, perform an update
+    if (d->lastModifiedTime < remoteModTime)
+    {
+      qDebug()<<"Object maybe overwritten on server!";
+      // TODO update from server, since modification time is not really supported
+      // by xnat right now this is not of high priority
+      // something like this->updateImpl + setLastModifiedTime()
+    }
   }
 
   // Creating the update query
@@ -342,10 +354,13 @@ void ctkXnatObject::commit ()
   const QList<QVariantMap> results = this->session()->httpSync(queryID);
 
   // If this xnat object did not exist before on the server set the ID returned by Xnat
-  QVariant id = results[0]["ID"];
-  if (id.isValid())
+  if (results.size() == 1 && results[0].size() == 1)
   {
-    this->setProperty("ID", id.toString());
+    QVariant id = results[0][ID];
+    if (!id.isNull())
+    {
+      this->setId(id.toString());
+    }
   }
 
   // Finally update the modification time on the server

+ 4 - 5
Libs/XNAT/Core/ctkXnatObject.h

@@ -129,11 +129,6 @@ public:
 
   void download(const QString&);
 
-  /// Sends the object to the XNAT server
-  /// Subclasses of ctkXnatObject can overwrite this function if needed
-  virtual void commit();
-
-  virtual void upload(const QString&);
 
   //QObject* asyncObject() const;
 
@@ -186,6 +181,10 @@ private:
   /// The implementation of the download mechanism, called by the download(const QString&) function.
   virtual void downloadImpl(const QString&) = 0;
 
+  /// The implementation of the upload mechanism, called by the upload() function.
+  /// Subclasses of ctkXnatObject can overwrite this function if needed
+  virtual void saveImpl();
+
   Q_DECLARE_PRIVATE(ctkXnatObject)
 };
 

+ 21 - 2
Libs/XNAT/Core/ctkXnatResource.cpp

@@ -51,7 +51,7 @@ ctkXnatResource::~ctkXnatResource()
 //----------------------------------------------------------------------------
 QString ctkXnatResource::resourceUri() const
 {
-  return QString("%1/%2").arg(parent()->resourceUri(), this->id());
+  return QString("%1/%2").arg(parent()->resourceUri(), this->name());
 }
 
 //----------------------------------------------------------------------------
@@ -127,11 +127,30 @@ void ctkXnatResource::downloadImpl(const QString& filename)
   this->session()->download(filename, query, parameters);
 }
 
-void ctkXnatResource::createFolder()
+//----------------------------------------------------------------------------
+void ctkXnatResource::saveImpl()
 {
   if (!this->session()->exists(this))
   {
+    QString query = this->resourceUri();
+    query.append("/");
+    query.append(this->name());
+
+    query.append(QString("?%1=%2").arg("xsi:type", this->schemaType()));
+    const QMap<QString, QString>& properties = this->properties();
+    QMapIterator<QString, QString> itProperties(properties);
+    while (itProperties.hasNext())
+    {
+      itProperties.next();
+      if (itProperties.key() == "ID")
+        continue;
+      query.append(QString("&%1=%2").arg(itProperties.key(), itProperties.value()));
+    }
     QUuid queryId = this->session()->httpPut(this->resourceUri());
     session()->httpResults(queryId, ctkXnatDefaultSchemaTypes::XSI_RESOURCE);
   }
+  else
+  {
+    // TODO Update resource
+  }
 }

+ 1 - 1
Libs/XNAT/Core/ctkXnatResource.h

@@ -58,7 +58,7 @@ public:
 
   void reset();
 
-  void createFolder();
+  void saveImpl();
 
   static const QString ID;
 

+ 7 - 0
Libs/XNAT/Core/ctkXnatResourceFolder.cpp

@@ -99,3 +99,10 @@ void ctkXnatResourceFolder::downloadImpl(const QString& filename)
   parameters["format"] = "zip";
   this->session()->download(filename, query, parameters);
 }
+
+//----------------------------------------------------------------------------
+void ctkXnatResourceFolder::saveImpl()
+{
+  // Not implemented since a resource folder is automatically created when
+  // a resource is uploaded
+}

+ 1 - 0
Libs/XNAT/Core/ctkXnatResourceFolder.h

@@ -49,6 +49,7 @@ private:
   friend class qRestResult;
   virtual void fetchImpl();
   virtual void downloadImpl(const QString&);
+  virtual void saveImpl();
 
   Q_DECLARE_PRIVATE(ctkXnatResourceFolder)
 };

+ 6 - 8
Libs/XNAT/Core/ctkXnatSession.cpp

@@ -607,16 +607,14 @@ void ctkXnatSession::download(const QString& fileName,
   d->xnat->sync(queryId);
 }
 
-void ctkXnatSession::upload(ctkXnatFile *file)
+//----------------------------------------------------------------------------
+void ctkXnatSession::upload(const QString &fileName,
+                            const QString &resource,
+                            const UrlParameters &parameters,
+                            const HttpRawHeaders &rawHeaders)
 {
   Q_D(ctkXnatSession);
-
-  QString query = file->resourceUri();
-  QString filename = file->property("filename");
-  qRestAPI::Parameters parameters;
-  parameters["inbody"] = "true";
-  parameters["tags"] = file->property("tags");
-  QUuid queryId = d->xnat->upload(filename, query, parameters);
+  QUuid queryId = d->xnat->upload(fileName, resource, parameters);
   d->xnat->sync(queryId);
 }
 

+ 10 - 1
Libs/XNAT/Core/ctkXnatSession.h

@@ -228,7 +228,16 @@ public:
     const UrlParameters& parameters = UrlParameters(),
     const HttpRawHeaders& rawHeaders = HttpRawHeaders());
 
-  void upload(ctkXnatFile* file);
+  /// Uploads a file to the web service.
+  /// \a fileName is the name of the file.
+  /// The \a resource and \parameters are used to compose the URL.
+  /// \a rawHeaders can be used to set the raw headers of the request to send.
+  /// These headers will be set additionally to those defined by the
+  /// \a defaultRawHeaders property.
+  void upload(const QString& fileName,
+    const QString& resource,
+    const UrlParameters& parameters = UrlParameters(),
+    const HttpRawHeaders& rawHeaders = HttpRawHeaders());
 
   /**
    * @brief Sends a http HEAD request to the xnat instance