Commit b36a9dc1 authored by John Tourtellott's avatar John Tourtellott
Browse files

Copy jobs panel source files from smtk:master

(without history)
parent 6ebd3bab
find_package(Qt5 REQUIRED COMPONENTS Core Gui Network Widgets)
include_directories(${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR})
include_directories("${CMAKE_CURRENT_SOURCE_DIR}/girderfilebrowser")
include_directories("${CMAKE_CURRENT_SOURCE_DIR}/girderfilebrowser/ui")
set(ui_files
ui/jobtablewidget.ui
ui/mainwindow.ui
ui/logindialog.ui
ui/cumuluswidget.ui
# Mandatory girder file browser files
girderfilebrowser/ui/girderfilebrowserdialog.ui
# Optional girder file browser files (for stand-alone executable)
girderfilebrowser/ui/girderlogindialog.ui
)
set(src
jobtablewidget.cxx
jobview.cxx
logindialog.cxx
jobtablemodel.cxx
job.cxx
cumulusproxy.cxx
girderrequest.cxx
jobrequest.cxx
utils.cxx
cumuluswidget.cxx
mainwindow.cxx
pqCumulusJobsPanel.cxx
# Mandatory girder file browser files
girderfilebrowser/girderfilebrowserfetcher.cxx
girderfilebrowser/ui/girderfilebrowserdialog.cxx
girderfilebrowser/ui/girderfilebrowserlistview.cxx
# Optional girder file browser files (for stand-alone executable)
girderfilebrowser/girderauthenticator.cxx
girderfilebrowser/ui/girderlogindialog.cxx
)
#add moc files
set(mochdrs
cumulusproxy.h
girderrequest.h
jobrequest.h
jobtablemodel.h
jobtablewidget.h
jobview.h
mainwindow.h
pqCumulusJobsPanel.h
# Mandatory girder file browser files
girderfilebrowser/girderfilebrowserfetcher.h
girderfilebrowser/ui/girderfilebrowserdialog.h
girderfilebrowser/ui/girderfilebrowserlistview.h
# Optional girder file browser files (for stand-alone executable)
girderfilebrowser/girderauthenticator.h
girderfilebrowser/ui/girderlogindialog.h
)
#resource files
set(resources
girderfilebrowser/resources.qrc
)
set(CMAKE_AUTOUIC 1)
set(CMAKE_AUTOMOC 1)
set(CMAKE_AUTORCC 1)
#install headers
set(hdrs
cumuluswidget.h
job.h
logindialog.h
)
#extension library
add_library(smtkCumulusExt
${src}
${hdrs}
${mochdrs}
${ui_files}
${resources}
)
smtk_public_headers(smtkCumulusExt ${hdrs})
#we need to add the location of the moc files to the include dir
target_include_directories(smtkCumulusExt PRIVATE ${CMAKE_CURRENT_BINARY_DIR})
#publicly link to smtkCore
target_link_libraries(smtkCumulusExt LINK_PUBLIC
smtkCore
Qt5::Core
Qt5::Gui
Qt5::Network
Qt5::Widgets
)
smtk_export_header(smtkCumulusExt Exports.h)
#install the library and exports
smtk_install_library(smtkCumulusExt)
#standalone cumulus executable
add_executable(cumulus MACOSX_BUNDLE
main.cxx
)
#standalone girder file browser executable for debugging
add_executable(girderfilebrowser MACOSX_BUNDLE WIN32
girderfilebrowser/girderfilebrowser.cxx
)
set_target_properties(cumulus PROPERTIES AUTOMOC TRUE)
set_target_properties(girderfilebrowser PROPERTIES AUTOMOC TRUE)
target_link_libraries(cumulus
smtkCumulusExt
cJSON
)
target_link_libraries(girderfilebrowser
smtkCumulusExt
)
if(SMTK_ENABLE_PARAVIEW_SUPPORT)
add_paraview_dock_window(cumulusJobsPanelIfaces cumulusJobsPanelIfaceSrcs
CLASS_NAME pqCumulusJobsPanel
)
list(APPEND PLUGIN_DOCK_IFACES ${cumulusJobsPanelIfaces})
list(APPEND PLUGIN_DOCK_IFACES_SRCS ${cumulusJobsPanelIfaceSrcs})
unset(CMAKE_AUTOMOC)
add_paraview_plugin(
smtkPQCumulusJobsPlugin "1.0"
GUI_INTERFACES
${PLUGIN_ACTION_IFACES}
${PLUGIN_AUTOSTART_IFACES}
${PLUGIN_PROXY_IFACES}
${PLUGIN_DOCK_IFACES}
GUI_SOURCES
${Headers}
${Sources}
${PLUGIN_MOC_SRCS}
${PLUGIN_ACTION_IFACE_SRCS}
${PLUGIN_AUTOSTART_IFACE_SRCS}
${PLUGIN_PROXY_IFACE_SRCS}
${PLUGIN_DOCK_IFACES_SRCS}
)
set_property(GLOBAL APPEND PROPERTY SMTK_PLUGINS "smtkPQCumulusJobsPlugin")
target_link_libraries(smtkPQCumulusJobsPlugin
LINK_PUBLIC
smtkCumulusExt
)
smtk_install_library(smtkPQCumulusJobsPlugin)
endif()
//=========================================================================
// Copyright (c) Kitware, Inc.
// All rights reserved.
// See LICENSE.txt for details.
//
// This software is distributed WITHOUT ANY WARRANTY; without even
// the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
// PURPOSE. See the above copyright notice for more information.
//=========================================================================
#include "cumulusproxy.h"
#include "cJSON.h"
#include "girderrequest.h"
#include "job.h"
#include "jobrequest.h"
#include "utils.h"
#include <QtCore/QCoreApplication>
#include <QtCore/QDebug>
#include <QtCore/QList>
#include <QtCore/QThread>
#include <QtCore/QUrlQuery>
#include <QtCore/QVariant>
#include <QtNetwork/QNetworkAccessManager>
#include <QtNetwork/QNetworkCookie>
#include <QtNetwork/QNetworkReply>
#include <QtNetwork/QNetworkRequest>
namespace
{
// Local class to provide sleep method for wait loops
class qtSleeper : public QThread
{
public:
static void msleep(unsigned long msecs) { QThread::msleep(msecs); }
};
} // namespace
namespace cumulus
{
CumulusProxy::CumulusProxy(QObject* parent)
: QObject(parent)
, m_networkManager(new QNetworkAccessManager(this))
{
}
CumulusProxy::~CumulusProxy()
{
}
void CumulusProxy::girderUrl(const QString& url)
{
m_girderUrl = url;
}
bool CumulusProxy::isGirderRunning(int timeoutSec)
{
bool running = false; // return value
// Request system version
QString girderVersionUrl = QString("%1/system/version").arg(m_girderUrl);
QNetworkRequest request(girderVersionUrl);
QNetworkReply* reply = m_networkManager->get(request);
// Wait for reply to finish
qtSleeper sleeper;
int timeoutMsec = timeoutSec * 1000;
int deltaMsec = 100;
sleeper.msleep(deltaMsec);
for (int waitMsec = 0; !reply->isFinished() && waitMsec < timeoutMsec; waitMsec += deltaMsec)
{
QCoreApplication::sendPostedEvents();
#ifdef WIN32
QCoreApplication::processEvents();
#endif
//qDebug() << "wait" << waitMsec << "msec";
sleeper.msleep(deltaMsec);
}
// Check for timeout
if (reply->isFinished() && reply->error() == QNetworkReply::NoError)
{
running = true;
}
reply->deleteLater();
return running;
}
void CumulusProxy::authenticateNewt(const QString& username, const QString& password)
{
QUrl url("https://newt.nersc.gov/newt/auth");
QNetworkRequest request(url);
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded");
QUrlQuery params;
params.addQueryItem("username", username);
params.addQueryItem("password", password);
auto reply = m_networkManager->post(request, params.query().toUtf8());
QObject::connect(reply, SIGNAL(finished()), this, SLOT(authenticationNewtFinished()));
QObject::connect(reply, SIGNAL(sslErrors(const QList<QSslError>&)), this,
SLOT(sslErrors(const QList<QSslError>&)));
}
void CumulusProxy::authenticationNewtFinished()
{
auto reply = qobject_cast<QNetworkReply*>(this->sender());
QByteArray bytes = reply->readAll();
if (reply->error())
{
emit error(handleGirderError(reply, bytes), reply);
}
else
{
cJSON* reply = cJSON_Parse(bytes.constData());
bool auth = cJSON_GetObjectItem(reply, "auth")->valueint == 1;
if (auth)
{
char* sessionId = cJSON_GetObjectItem(reply, "newt_sessionid")->valuestring;
m_newtSessionId.clear();
m_newtSessionId.append(sessionId);
// Now authenticate with Girder
this->authenticateGirder(m_newtSessionId);
}
else
{
emit newtAuthenticationError(QString("Invalid login"));
}
cJSON_Delete(reply);
}
reply->deleteLater();
}
void CumulusProxy::authenticateGirder(const QString& /*newtSessionId*/)
{
m_girderToken.clear();
QString girderAuthUrl = QString("%1/newt/authenticate/%2").arg(m_girderUrl).arg(m_newtSessionId);
QNetworkRequest request(girderAuthUrl);
QByteArray empty;
auto reply = m_networkManager->put(request, empty);
QObject::connect(reply, SIGNAL(finished()), this, SLOT(authenticationGirderFinished()));
}
void CumulusProxy::authenticationGirderFinished()
{
auto reply = qobject_cast<QNetworkReply*>(this->sender());
QByteArray bytes = reply->readAll();
if (reply->error())
{
emit error(handleGirderError(reply, bytes), reply);
}
else
{
QVariant v = reply->header(QNetworkRequest::SetCookieHeader);
QList<QNetworkCookie> cookies = qvariant_cast<QList<QNetworkCookie> >(v);
foreach (QNetworkCookie cookie, cookies)
{
if (cookie.name() == "girderToken")
{
m_girderToken = cookie.value();
}
}
if (m_girderToken.isEmpty())
{
emit error(QString("Girder response did not set girderToken"));
}
else
{
emit authenticationFinished();
}
}
reply->deleteLater();
}
void CumulusProxy::fetchJobs()
{
QString girderAuthUrl = QString("%1/jobs").arg(m_girderUrl);
QNetworkRequest request(girderAuthUrl);
request.setRawHeader(QByteArray("Girder-Token"), m_girderToken.toUtf8());
auto reply = m_networkManager->get(request);
QObject::connect(reply, SIGNAL(finished()), this, SLOT(fetchJobsFinished()));
}
void CumulusProxy::fetchJobsFinished()
{
auto reply = qobject_cast<QNetworkReply*>(this->sender());
QByteArray bytes = reply->readAll();
if (reply->error())
{
emit error(handleGirderError(reply, bytes), reply);
}
else
{
//qDebug() << "fetchJobsReply:" << bytes.constData();
cJSON* jsonResponse = cJSON_Parse(bytes.constData());
if (jsonResponse && jsonResponse->type == cJSON_Array)
{
QList<Job> jobs;
for (cJSON* jsonJob = jsonResponse->child; jsonJob; jsonJob = jsonJob->next)
{
Job job = Job::fromJSON(jsonJob);
if (job.isValid())
{
jobs.append(job);
}
else
{
emit error(QString("Error parsing job JSON."));
}
}
emit jobsUpdated(jobs);
}
else
{
emit error(QString("Girder send JSON response of the incorrect format."));
}
cJSON_Delete(jsonResponse);
}
reply->deleteLater();
}
bool CumulusProxy::isAuthenticated()
{
return !m_girderToken.isEmpty();
}
void CumulusProxy::fetchJob(const QString& id)
{
QString girderAuthUrl = QString("%1/jobs/%2").arg(m_girderUrl).arg(id);
QNetworkRequest request(girderAuthUrl);
request.setRawHeader(QByteArray("Girder-Token"), m_girderToken.toUtf8());
auto reply = m_networkManager->get(request);
QObject::connect(reply, SIGNAL(finished()), this, SLOT(fetchJobStatusFinished()));
}
void CumulusProxy::fetchJobFinished()
{
auto reply = qobject_cast<QNetworkReply*>(this->sender());
QByteArray bytes = reply->readAll();
if (reply->error())
{
emit error(handleGirderError(reply, bytes), reply);
}
else
{
cJSON* jsonResponse = cJSON_Parse(bytes.constData());
if (jsonResponse)
{
Job job = Job::fromJSON(jsonResponse);
if (job.isValid())
{
emit jobUpdated(job);
}
else
{
emit error(QString("Error parsing job JSON."));
}
}
else
{
emit error(QString("Girder send JSON response of the incorrect format."));
}
cJSON_Delete(jsonResponse);
}
reply->deleteLater();
}
void CumulusProxy::deleteJob(Job job)
{
DeleteJobRequest* request =
new DeleteJobRequest(m_networkManager, m_girderUrl, m_girderToken, job, this);
connect(request, SIGNAL(complete()), this, SLOT(deleteJobFinished()));
connect(request, SIGNAL(error(const QString, QNetworkReply*)), this,
SIGNAL(error(const QString, QNetworkReply*)));
request->send();
}
void CumulusProxy::deleteJobFinished()
{
DeleteJobRequest* request = qobject_cast<DeleteJobRequest*>(this->sender());
emit jobDeleted(request->job());
request->deleteLater();
this->fetchJobs();
}
void CumulusProxy::patchJobs(QList<Job> jobs)
{
// Patch only applies to cmb-specific data, stored as job metadata
foreach (Job job, jobs)
{
//qDebug() << "Patching job" << job.id();
cJSON* body = cJSON_CreateObject();
cJSON* cmbData = job.cmbDataToJSON();
cJSON_AddItemToObject(body, "metadata", cmbData);
PatchJobRequest* updateRequest =
new PatchJobRequest(m_networkManager, m_girderUrl, m_girderToken, job, body);
connect(updateRequest, SIGNAL(error(const QString, QNetworkReply*)), this,
SIGNAL(error(const QString, QNetworkReply*)));
connect(updateRequest, SIGNAL(info(const QString)), this, SIGNAL(info(const QString)));
updateRequest->send();
cJSON_Delete(body);
}
}
void CumulusProxy::terminateJob(Job job)
{
TerminateJobRequest* request =
new TerminateJobRequest(m_networkManager, m_girderUrl, m_girderToken, job, this);
connect(request, SIGNAL(complete()), this, SLOT(terminateJobFinished()));
connect(request, SIGNAL(error(const QString, QNetworkReply*)), this,
SIGNAL(error(const QString, QNetworkReply*)));
request->send();
}
void CumulusProxy::terminateJobFinished()
{
TerminateJobRequest* request = qobject_cast<TerminateJobRequest*>(sender());
emit jobTerminated(request->job());
request->deleteLater();
this->fetchJobs();
}
void CumulusProxy::sslErrors(const QList<QSslError>& /*errors*/)
{
auto reply = qobject_cast<QNetworkReply*>(this->sender());
emit error(reply->errorString());
reply->deleteLater();
}
void CumulusProxy::downloadJob(const QString& downloadDirectory, Job job)
{
DownloadJobRequest* request = new DownloadJobRequest(
m_networkManager, m_girderUrl, m_girderToken, downloadDirectory, job, this);
connect(request, SIGNAL(complete()), this, SLOT(downloadJobFinished()));
connect(request, SIGNAL(error(const QString, QNetworkReply*)), this,
SIGNAL(error(const QString, QNetworkReply*)));
connect(request, SIGNAL(info(const QString)), this, SIGNAL(info(const QString)));
request->send();
}
void CumulusProxy::downloadJobFinished()
{
DownloadJobRequest* request = qobject_cast<DownloadJobRequest*>(sender());
Job job = request->job();
job.setDownloadFolder(request->path());
emit jobDownloaded(request->job(), request->path());
emit info("Job download complete.");
request->deleteLater();
// Update status to "downloaded"
cJSON* body = cJSON_CreateObject();
cJSON_AddStringToObject(body, "status", "downloaded");
// Update cmb-specific data
cJSON* cmbData = job.cmbDataToJSON();
cJSON_AddItemToObject(body, "metadata", cmbData);
PatchJobRequest* updateRequest =
new PatchJobRequest(m_networkManager, m_girderUrl, m_girderToken, request->job(), body);
connect(updateRequest, SIGNAL(error(const QString, QNetworkReply*)), this,
SIGNAL(error(const QString, QNetworkReply*)));
connect(updateRequest, SIGNAL(info(const QString)), this, SIGNAL(info(const QString)));
updateRequest->send();
cJSON_Delete(body);
}
} // end namespace
//=========================================================================
// Copyright (c) Kitware, Inc.
// All rights reserved.
// See LICENSE.txt for details.
//
// This software is distributed WITHOUT ANY WARRANTY; without even
// the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
// PURPOSE. See the above copyright notice for more information.
//=========================================================================
// .NAME cumulusproxy.h
// .SECTION Description
// .SECTION See Also
#ifndef __smtk_extension_cumulus_cumulusproxy_h
#define __smtk_extension_cumulus_cumulusproxy_h
#include "smtk/extension/cumulus/Exports.h"
#include "smtk/extension/cumulus/job.h"
#include <QList>
#include <QObject>
class QNetworkCookieJar;
class QNetworkReply;
class QSslError;
class QNetworkAccessManager;
namespace cumulus
{
class SMTKCUMULUSEXT_EXPORT CumulusProxy : public QObject
{
Q_OBJECT
public:
CumulusProxy(QObject* parent = 0);
~CumulusProxy();
void girderUrl(const QString& url);
bool isGirderRunning(int timeoutSec = 5);
public slots:
void authenticateGirder(const QString& newtSessionId);
void authenticateNewt(const QString& username, const QString& password);
bool isAuthenticated();
void fetchJobs();
void fetchJob(const QString& id);
void deleteJob(Job job);
void patchJobs(QList<Job> jobs);
void terminateJob(Job job);
void downloadJob(const QString& downloadDirectory, Job job);
signals:
void jobsUpdated(QList<Job> jobs);