Initial commit
This commit is contained in:
commit
b7d90b9afe
16245 files changed
+3396666
No files matched your search
@@ -0,0 +1,43 @@
|
||||
#
|
||||
# $Id: CMakeLists.txt 51804 2020-07-31 04:47:28Z fritsche_h $
|
||||
# $HeadURL: svn://slssvn/TDS/branches/product/iterations/21_03_salted_caramel/Lts/Plugins/TakeHomePackageManager/CMakeLists.txt $
|
||||
# $Author: fritsche_h $
|
||||
#
|
||||
# Created by Mike Wille, SLTE, 2020-03-23
|
||||
#
|
||||
|
||||
SET(src
|
||||
logger.cpp
|
||||
logger.h
|
||||
tpm_exported_scripts_model.cpp
|
||||
tpm_exported_scripts_model.h
|
||||
tpm_process_dialog.cpp
|
||||
tpm_process_dialog.h
|
||||
)
|
||||
|
||||
QT5_WRAP_UI(src
|
||||
tpm_process_dialog.ui
|
||||
)
|
||||
|
||||
NGL_ADD_LIBRARY2(WS ThpCommon SHARED ${src})
|
||||
NGL_TARGET_INCLUDE_DIRECTORIES(ThpCommon
|
||||
PUBLIC
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../
|
||||
${CMAKE_CURRENT_BINARY_DIR}/../ # export file
|
||||
)
|
||||
NGL_TARGET_LINK_LIBRARIES(ThpCommon
|
||||
Qt5::Core
|
||||
Qt5::Concurrent
|
||||
NGLLogger
|
||||
LtsSession
|
||||
AsmDataPluginInterface
|
||||
ExmDataPluginInterface
|
||||
#nglcompression
|
||||
#nglsettings
|
||||
#ngltools
|
||||
#LtsSessionData
|
||||
#httq
|
||||
)
|
||||
|
||||
NGL_INSTALL_TARGET(ThpCommon)
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
* Building Config - logging facility
|
||||
* $Id: logger.cpp 46225 2019-03-25 13:59:56Z rudolphus_g $
|
||||
* $HeadURL: svn://slssvn/TDS/branches/product/iterations/21_03_salted_caramel/Lts/Plugins/TakeHomePackageManager/logger.cpp $
|
||||
* $Author: rudolphus_g $
|
||||
*/
|
||||
|
||||
#include "logger.h"
|
||||
|
||||
#include <NGLLogger/NGLLoggerBootstrap.h>
|
||||
|
||||
namespace TakeHomePackageManager
|
||||
{
|
||||
|
||||
LOGGER_T getLogger(const char *name)
|
||||
{
|
||||
static const char ns[] = "TPM.Common.";
|
||||
std::string loggerName;
|
||||
const size_t len = strlen(name);
|
||||
loggerName.reserve(sizeof(ns) + len);
|
||||
loggerName.append(ns, sizeof(ns) - 1);
|
||||
loggerName.append(name, len);
|
||||
|
||||
try
|
||||
{
|
||||
return NGL::NGLLoggerBootstrap::getNGLRootLogger().
|
||||
getNGLLogger(loggerName);
|
||||
}
|
||||
catch (const std::exception &ex)
|
||||
{
|
||||
fprintf(stderr, "Error retrieving logger: %s", ex.what());
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace TakeHomePackageManager
|
||||
@@ -0,0 +1,22 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
* Logging for CentralEffectSimulation
|
||||
* $Id: logger.h 46225 2019-03-25 13:59:56Z rudolphus_g $
|
||||
* $HeadURL: svn://slssvn/TDS/branches/product/iterations/21_03_salted_caramel/Lts/Plugins/TakeHomePackageManager/logger.h $
|
||||
* $Author: rudolphus_g $
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <NGLLogger/INGLLogger.h>
|
||||
|
||||
namespace TakeHomePackageManager
|
||||
{
|
||||
typedef NGL::INGLLogger &LOGGER_T;
|
||||
|
||||
LOGGER_T getLogger(const char *name);
|
||||
inline LOGGER_T getLogger(const QString &loggerName)
|
||||
{
|
||||
return getLogger(loggerName.toUtf8().constData());
|
||||
}
|
||||
} // namespace TakeHomePackageManager
|
||||
+632
@@ -0,0 +1,632 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
* Show the exported scripts and allow them to upload them to the server
|
||||
* $Id: tpm_exported_scripts_model.cpp 53111 2021-03-22 08:21:29Z wille_m $
|
||||
* $HeadURL: svn://slssvn/TDS/branches/product/iterations/21_03_salted_caramel/Lts/Plugins/TakeHomePackageManager/tpm_exported_scripts_model.cpp $
|
||||
* $Author: wille_m $
|
||||
* Created by Gerlinde Rudolphus, NLE, 2019-03-25
|
||||
*/
|
||||
|
||||
#include "tpm_exported_scripts_model.h"
|
||||
|
||||
#include <ngltools/ngltools_dtg.h>
|
||||
|
||||
#include <QDateTime>
|
||||
#include <QDir>
|
||||
#include <QFile>
|
||||
#include <QFileInfo>
|
||||
#include <QJsonArray>
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
|
||||
|
||||
namespace TakeHomePackageManager
|
||||
{
|
||||
|
||||
ExportedScriptsModel::ExportedScriptsModel(QObject *parent)
|
||||
: LtsWidgets::LtsAbstractTableModel<ExportedScriptInformation>(
|
||||
{ tr("id"), tr("Exercise"), tr("Name"), tr("Created"), tr("Last downloaded"), tr("Location"), tr("Last uploaded"), tr("Server")}, parent)
|
||||
, m_logger(getLogger("ExportedScriptsModel"))
|
||||
{}
|
||||
|
||||
bool ExportedScriptsModel::init(const QString &rootDirectoryToScan)
|
||||
{
|
||||
std::shared_ptr<int> _(nullptr, [this](int *) { endResetModel(); }); // TODO: QScopeGuard
|
||||
m_rootDirectoryToScan = rootDirectoryToScan;
|
||||
beginResetModel();
|
||||
m_vecData.clear();
|
||||
|
||||
QVector <int> localExercises;
|
||||
DEBUG_LOG(m_logger) << "ExportedScriptsModel::init rootDirectoryToScan=" << rootDirectoryToScan
|
||||
/*<< " directoryToScan=" << directoryToScan*/;
|
||||
|
||||
//clear(); multiple inits needed, don't clear
|
||||
|
||||
// die Skripte aller Uebungen auf der lokalen Workstation anzeigen.
|
||||
// Die Uebungen sind in der Datei /tmp/THP-xxxxxxx/data.json im Array Exercises enthalten.
|
||||
{
|
||||
DEBUG_LOG(m_logger) << "Get all local exercises rootDirectoryToScan=" << rootDirectoryToScan ;
|
||||
QFile file(rootDirectoryToScan + QDir::separator() + QStringLiteral("exercises.json"));
|
||||
if (!file.open(QFile::ReadOnly)) // file only exists after a first download, but that's okay
|
||||
WARNING_LOG(m_logger) << "Cannot open " << file.fileName() << " for read/write: " << file.errorString();
|
||||
|
||||
const QJsonDocument doc(QJsonDocument::fromJson(file.readAll()));
|
||||
const QJsonObject obj = doc.object(); // okay to be empty
|
||||
const QJsonArray arr = obj[QStringLiteral("Exercises")].toArray();
|
||||
localExercises.reserve(arr.size());
|
||||
for (const auto &ele : arr)
|
||||
localExercises.append(ele.toInt());
|
||||
}
|
||||
|
||||
WARNING_LOG(m_logger) << "local exericses: " << localExercises.count();
|
||||
|
||||
// Alle lokalen Übungen:
|
||||
for (const int exId : qAsConst(localExercises))
|
||||
{
|
||||
const QString baseDir = exercisePath(rootDirectoryToScan, exId);
|
||||
QFile file(baseDir + QDir::separator() + QStringLiteral("data.json"));
|
||||
|
||||
if (!file.open(QIODevice::ReadOnly))
|
||||
{
|
||||
WARNING_LOG(m_logger) << "Cannot open " << file.fileName() << " for reading: " << file.errorString() << " - not critical";
|
||||
// There are no local scripts but maybe some are on the server
|
||||
return true;
|
||||
}
|
||||
|
||||
const QJsonDocument doc(QJsonDocument::fromJson(file.readAll()));
|
||||
const QJsonObject obj = doc.object();
|
||||
const QJsonArray presentations = obj[QStringLiteral("presentations")].toArray();
|
||||
|
||||
DEBUG_LOG(m_logger) << "exercise: " << exId << ", presentations: " << presentations.size();
|
||||
|
||||
for (const QJsonValue &val : presentations)
|
||||
{
|
||||
ExportedScriptInformation esi = ExportedScriptInformation::fromJson(val);
|
||||
DEBUG_LOG(m_logger) << "obj=" << QJsonDocument(obj).toJson(QJsonDocument::Compact);
|
||||
const QJsonObject presentationObj = val.toObject();
|
||||
DEBUG_LOG(m_logger) << "presentationObj=" << QJsonDocument(presentationObj).toJson(QJsonDocument::Compact);
|
||||
if (obj.contains(QStringLiteral("exerciseId")))
|
||||
{
|
||||
esi.exerciseId = obj[QStringLiteral("exerciseId")].toInt();
|
||||
esi.exerciseName = obj[QStringLiteral("exerciseName")].toString();
|
||||
if (presentationObj.contains(QStringLiteral("name")) )
|
||||
esi.scriptName = presentationObj[QStringLiteral("name")].toString();
|
||||
if (presentationObj.contains(QStringLiteral("displayName")) )
|
||||
esi.scriptName = presentationObj[QStringLiteral("displayName")].toString();
|
||||
}
|
||||
|
||||
esi.local = true;
|
||||
DEBUG_LOG(m_logger) << "push_back "
|
||||
<< " exportTime=" << esi.exportTime
|
||||
<< " scriptId=" << esi.scriptId
|
||||
<< " scriptName=" << esi.scriptName
|
||||
<< " exerciseId=" << esi.exerciseId
|
||||
<< " exerciseName=" << esi.exerciseName
|
||||
<< " local=" << esi.local;
|
||||
esi.uploaded = ThpUnknownUploadState;
|
||||
|
||||
esi.rootDirectory = rootDirectoryToScan; // to determine if this script is from THP-Download or THP-Export directory (for USB Management, which can copy from both dirs)
|
||||
|
||||
m_vecData.push_back(esi);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief ExportedScriptsModel::setServerContent fills the table with local and
|
||||
* server scripts and deletes rows of deleted scripts.
|
||||
* @param jsonArray of scripts from the server
|
||||
* @return true if jsonArray could be parsed and was processed, false if
|
||||
* jsonArray could not be parsed
|
||||
*/
|
||||
void ExportedScriptsModel::setServerContent(const QString &jsonArray)
|
||||
{
|
||||
if (jsonArray.isEmpty()) // no content received yet? no problem, just return
|
||||
return;
|
||||
|
||||
/* these vectors are needed to compare which scripts are already
|
||||
* on the server or are local or are both on the server and local
|
||||
* e.g. if the script was downloaded after it was once uploaded
|
||||
* to the server */
|
||||
QMap<int, QVector<int>> exerciseContentScriptIds; // scripts from server - Key = ExerciseId, value = script ids of exercise
|
||||
QMap<int, QVector<int>> exerciseScriptIds; // scripts from local data.json file - Key = ExerciseId, value = script ids of exercise
|
||||
|
||||
DEBUG_LOG(m_logger) << "ExportedScriptsModel::setServerContent jsonArray =" << jsonArray;
|
||||
QJsonParseError err;
|
||||
const QJsonArray presentations = QJsonDocument::fromJson(jsonArray.toUtf8(), &err).array();
|
||||
DEBUG_LOG(m_logger) << "presentations.size() =" << presentations.size();
|
||||
|
||||
if (err.error != QJsonParseError::NoError)
|
||||
{
|
||||
ERROR_LOG(m_logger) << "json array invalid at offset " << err.offset << ": " << err.errorString();
|
||||
return;
|
||||
}
|
||||
|
||||
// put local scripts into local scripts list
|
||||
for (const auto &data : qAsConst(m_vecData))
|
||||
{
|
||||
exerciseScriptIds[data.exerciseId].append(data.scriptId);
|
||||
DEBUG_LOG(m_logger) << "exerciseId=" << data.exerciseId
|
||||
<< " modelScriptIds.append: " << data.scriptId;
|
||||
}
|
||||
|
||||
// put server scripts into server scripts list
|
||||
for (const QJsonValue &val : presentations)
|
||||
{
|
||||
const ExportedScriptInformation esi = ExportedScriptInformation::fromJson(val, false, ThpAlreadyUploaded);
|
||||
exerciseContentScriptIds[esi.exerciseId].append(esi.scriptId);
|
||||
}
|
||||
|
||||
/* look for scripts that are on the server but also on the local side to
|
||||
* either remove the corresponding row from the table if the script is not on
|
||||
* the server and not set as local (that makes it non-local and non-existant
|
||||
* on the server, which means it was at one point on the server, but was
|
||||
* deleted from it after the server script list was received here some
|
||||
* minutes ago) -
|
||||
* or to add the not-uploaded state to the table row if it's local only
|
||||
* (but what about local scripts that were uploaded? => This is handeled at
|
||||
* the next main for-loop below)
|
||||
*/
|
||||
for (int i = m_vecData.size() - 1; i >= 0; --i) // scriptId ist nur im Modell aber nicht im content
|
||||
{
|
||||
const auto &rowData = m_vecData[i];
|
||||
const QVector<int> contentScriptIds = exerciseContentScriptIds.value(rowData.exerciseId);
|
||||
DEBUG_LOG(m_logger) << "m_vecData.at(" << i << ").scriptId=" << rowData.scriptId
|
||||
<< " exerciseId=" << rowData.exerciseId
|
||||
<< " contentScriptIds.contains=" << contentScriptIds.contains(rowData.scriptId)
|
||||
<< " rowData.local=" << rowData.local
|
||||
<< " contentScriptIds.size()=" << contentScriptIds.size();
|
||||
|
||||
if (!contentScriptIds.contains(rowData.scriptId))
|
||||
{
|
||||
// the script already in the table is not available on the server side
|
||||
if (rowData.local == false)
|
||||
{
|
||||
/* script already in table is marked as non-loal, which means - in
|
||||
* combination with the script not being on the server (anymore) - that
|
||||
* the script was removed from the server after the server script list
|
||||
* was received from the server and added to the table. So remove it
|
||||
* from the table */
|
||||
DEBUG_LOG(m_logger) << "removeRow(" << i << ")"
|
||||
<< " ScriptId=" << rowData.scriptId
|
||||
<< " ExerciseId=" << rowData.exerciseId;
|
||||
removeRow(i);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* otherwise, if it's marked as local, that means - in combination
|
||||
* with he script not being on the server (anymore) - that the script
|
||||
* is only locally available */
|
||||
setUploadState(i, ThpUploadNotActive, 0, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* get all server scripts and add them to the table if they're not already in
|
||||
* the table.LtsUtils::ProjectSettings *ps = L
|
||||
* if the corresponding row already exist then set the uploaded-state to
|
||||
* uploaded, because they came from the server list, which means they were
|
||||
* obviously uploaded at some point */
|
||||
for (const QJsonValue &val : presentations)
|
||||
{
|
||||
ExportedScriptInformation esi = ExportedScriptInformation::fromJson(val, false, ThpAlreadyUploaded);
|
||||
QVector<int> modelScriptIds = exerciseScriptIds.value(esi.exerciseId);
|
||||
DEBUG_LOG(m_logger) << " exportTime=" << esi.exportTime
|
||||
<< " scriptId=" << esi.scriptId
|
||||
<< " exerciseId=" << esi.exerciseId
|
||||
<< " exerciseName=" << esi.exerciseName
|
||||
<< " scriptName=" << esi.scriptName
|
||||
<< " fileSize=" << esi.fileSize
|
||||
<< " modelScriptIds.size=" << modelScriptIds.size();
|
||||
|
||||
// if a new script was added on the server and not available in this model already, then add it to the model
|
||||
if (!modelScriptIds.contains(esi.scriptId))
|
||||
{
|
||||
DEBUG_LOG(m_logger) << "insertRow()";
|
||||
insertRow(esi);
|
||||
continue;
|
||||
}
|
||||
|
||||
// update model
|
||||
for (int i = 0; i < m_vecData.size(); i++)
|
||||
{
|
||||
const auto &rowData = m_vecData[i];
|
||||
if (rowData.scriptId == esi.scriptId &&
|
||||
rowData.exerciseId == esi.exerciseId)
|
||||
{
|
||||
DEBUG_LOG(m_logger) << "setUploadState i=" << i
|
||||
<< " esi.scriptId=" << esi.scriptId
|
||||
<< " esi.exerciseId=" << esi.exerciseId
|
||||
<< " esi.fileSize=" << esi.fileSize
|
||||
<< " esi.uploadTime=" << esi.uploadTime;
|
||||
setUploadState(i, ThpAlreadyUploaded, esi.uploadTime, esi.fileSize);
|
||||
break; // can only match 1 time
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
update();
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
qint64 ExportedScriptsModel::downloadTime(const ExportedScriptInformation &val) const
|
||||
{
|
||||
if (!val.local) // can't be downloaded if it's not local
|
||||
return 0;
|
||||
|
||||
QString filename(QStringLiteral("%1/THP-Download/Exercise_%2/%3/index.html").arg(QDir::tempPath()).arg(val.exerciseId).arg(val.scriptId));
|
||||
DEBUG_LOG(m_logger) << "downloadTime: filename " << filename;
|
||||
|
||||
QFileInfo fi(filename);
|
||||
if (!fi.exists())
|
||||
return 0;
|
||||
|
||||
return fi.lastModified().toMSecsSinceEpoch();
|
||||
}
|
||||
|
||||
|
||||
QVariant ExportedScriptsModel::data(const QModelIndex &index, int role) const
|
||||
{
|
||||
if (!indexIsValid(index))
|
||||
return QVariant();
|
||||
|
||||
const ExportedScriptInformation &val = m_vecData[index.row()];
|
||||
|
||||
switch (role)
|
||||
{
|
||||
case Qt::FontRole:
|
||||
{
|
||||
if (!m_isOC
|
||||
/* because non-oc app has no "downloaded" column, telling the
|
||||
* user he has a newer version to download (by making the uploaded
|
||||
* cell fat) makes no sense */
|
||||
|
||||
|| !val.local
|
||||
/* non-downloaded scripts can't be older than the script on the server
|
||||
* because there obviously is no downloaded (local) script */
|
||||
|
||||
|| index.column() != ColUploadedTime
|
||||
|
||||
|| val.uploadTime <= (downloadTime(val) + (10 * 1000) /* 10 second grace */))
|
||||
/* if the script was downloaded from the server before the script was
|
||||
* updated / uploaded to the server again, mark the uploaded time cell
|
||||
* as fat to indicate the local, downloaded script is outdated */
|
||||
break;
|
||||
|
||||
QFont font;
|
||||
font.setBold(true);
|
||||
return font;
|
||||
}
|
||||
|
||||
case Qt::DisplayRole:
|
||||
{
|
||||
switch (index.column())
|
||||
{
|
||||
case ColId:
|
||||
return val.scriptId;
|
||||
case ColExercise:
|
||||
return val.exerciseName;
|
||||
case ColName:
|
||||
return val.scriptName;
|
||||
case ColCreated:
|
||||
return QDateTime::fromMSecsSinceEpoch(val.exportTime);
|
||||
case ColDownloaded:
|
||||
{
|
||||
qint64 dlTime = downloadTime(val);
|
||||
if (dlTime == 0)
|
||||
return QVariant();
|
||||
return QDateTime::fromMSecsSinceEpoch(dlTime);
|
||||
}
|
||||
case ColUploadedTime:
|
||||
return QDateTime::fromMSecsSinceEpoch(val.uploadTime);
|
||||
case ColLocation:
|
||||
return val.local ? tr("Local") : tr("Server");
|
||||
case ColUploaded:
|
||||
switch (val.uploaded)
|
||||
{
|
||||
case ThpConnectToHost:
|
||||
return tr("Connect to host");
|
||||
case ThpUploadActive:
|
||||
return tr("Upload Active");
|
||||
case ThpUploadError:
|
||||
return tr("Upload Error");
|
||||
case ThpUploadNotActive:
|
||||
return tr("Not uploaded");
|
||||
case ThpUnknownUploadState:
|
||||
return tr("Unknown upload status");
|
||||
case ThpAlreadyUploaded:
|
||||
return tr("Already uploaded");
|
||||
}
|
||||
}
|
||||
|
||||
case Qt::UserRole:
|
||||
{
|
||||
return val.scriptId;
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
bool ExportedScriptsModel::createDataJson(const QString &rootDirectory,
|
||||
const std::shared_ptr<ExerciseManagement::Interface::Dtc> &exmDtc,
|
||||
const std::shared_ptr<AssetManagement::Interface::Dtc> &asmDtc,
|
||||
int exerciseId) const // TODO: static, static logger
|
||||
{
|
||||
const QString baseDir = exercisePath(rootDirectory, exerciseId);
|
||||
|
||||
if (!QDir().mkpath(baseDir)) // NOTE: returns true if already exists
|
||||
{
|
||||
ERROR_LOG(m_logger) << "Failed to create path " << baseDir;
|
||||
return false;
|
||||
}
|
||||
|
||||
QFile file(baseDir + QStringLiteral("data.json"));
|
||||
DEBUG_LOG(m_logger) << "createDataJson=" << file.fileName();
|
||||
if (!file.open(QFile::ReadWrite))
|
||||
{
|
||||
QString msg(tr("Could not open data file in script directory: %1").arg(file.errorString()));
|
||||
ERROR_LOG(m_logger) << msg << " - " << file.fileName();
|
||||
return false;
|
||||
}
|
||||
|
||||
QJsonDocument doc(QJsonDocument::fromJson(file.readAll()));
|
||||
QJsonObject obj = doc.object();
|
||||
if (obj[QStringLiteral("exerciseId")].toInt() == exerciseId)
|
||||
{
|
||||
DEBUG_LOG(m_logger) << baseDir << " exists for exerciseId " << QString::number(exerciseId);
|
||||
return true;
|
||||
}
|
||||
|
||||
const ExerciseManagement::Exercise exercise = exmDtc->exercise(exerciseId);
|
||||
const AssetManagement::TrainingArea ta = asmDtc->trainingArea(exercise.trainingAreaId());
|
||||
|
||||
obj[QStringLiteral("title")] = QString::fromStdString(exercise.name());
|
||||
obj[QStringLiteral("exerciseId")] = exercise.id();
|
||||
obj[QStringLiteral("exerciseName")] = QString::fromStdString(exercise.name());
|
||||
obj[QStringLiteral("exerciseArea")] = QString::fromStdString(ta.name());
|
||||
obj[QStringLiteral("cycleTime")] = QString(QDateTime::fromMSecsSinceEpoch(exercise.beginTime()).toString() +
|
||||
QStringLiteral(" - ") + QDateTime::fromMSecsSinceEpoch(exercise.endTime()).toString());
|
||||
obj[QStringLiteral("exerciseStart")] = exercise.beginTime();
|
||||
obj[QStringLiteral("exerciseEnd")] = exercise.endTime();
|
||||
obj[QStringLiteral("exercisingForces")] = QString::fromStdString(exercise.forces());
|
||||
obj[QStringLiteral("exercisingPurpose")] = QString::fromStdString(exercise.description());
|
||||
|
||||
file.resize(0);
|
||||
file.write(QJsonDocument(obj).toJson());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void ExportedScriptsModel::addDownloadData(const ExportedScriptInformation &esi)
|
||||
{
|
||||
// Add exercise to ExportRootPath
|
||||
{
|
||||
// modify exercises.json
|
||||
DEBUG_LOG(m_logger) << "Add exercise to ExportRootPath";
|
||||
|
||||
if (!QDir(esi.rootDirectory).exists())
|
||||
{
|
||||
ERROR_LOG(m_logger) << "Cannot modify exercises.json as the info's root directory '"<<esi.rootDirectory<<"' does not exist!";
|
||||
return;
|
||||
}
|
||||
|
||||
QFile file(esi.rootDirectory + QDir::separator() + QStringLiteral("exercises.json"));
|
||||
if (!file.open(QFile::ReadWrite))
|
||||
{
|
||||
WARNING_LOG(m_logger) << "Can not open " << file.fileName() << " for read/write: " << file.errorString();
|
||||
return;
|
||||
}
|
||||
|
||||
const QString key = QStringLiteral("Exercises");
|
||||
QJsonDocument doc(QJsonDocument::fromJson(file.readAll()));
|
||||
QJsonObject obj = doc.object();
|
||||
QJsonArray arr = obj.value(key).toArray();
|
||||
|
||||
if (!arr.contains(esi.exerciseId))
|
||||
{
|
||||
DEBUG_LOG(m_logger) << "arr.append Exercise=" << esi.exerciseId;
|
||||
arr.append(esi.exerciseId);
|
||||
}
|
||||
|
||||
obj.insert(key, arr);
|
||||
file.resize(0);
|
||||
file.write(QJsonDocument(obj).toJson());
|
||||
INFO_LOG(m_logger) << " file.write " << QJsonDocument(obj).toJson();
|
||||
file.close();
|
||||
}
|
||||
|
||||
|
||||
// NOTE: mkdir actually not necessary because the decompressed bzip file of the script already created the exercise directory
|
||||
const QString baseDir = exercisePath(esi.rootDirectory, esi.exerciseId);
|
||||
if (!QDir().mkpath(baseDir)) // NOTE: returns true if dir already exists
|
||||
{
|
||||
ERROR_LOG(m_logger) << "Cannot create destination path " << baseDir;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// modify data.json
|
||||
QFile file(baseDir + QDir::separator() + QStringLiteral("data.json"));
|
||||
DEBUG_LOG(m_logger) << "File= " << file.fileName();
|
||||
if (!file.open(QFile::ReadWrite))
|
||||
{
|
||||
WARNING_LOG(m_logger) << "Can not open " << file.fileName() << " for read/write: " << file.errorString();
|
||||
return;
|
||||
}
|
||||
|
||||
const QString keyArray = QStringLiteral("presentations");
|
||||
const QJsonDocument doc(QJsonDocument::fromJson(file.readAll()));
|
||||
QJsonObject obj = doc.object();
|
||||
QJsonArray arr = obj.value(keyArray).toArray();
|
||||
bool exported = false;
|
||||
DEBUG_LOG(m_logger) << "arr.size=" << arr.size();
|
||||
|
||||
for (int i = 0; i < arr.size(); ++i)
|
||||
{
|
||||
auto arresi = ExportedScriptInformation::fromJson(arr.at(i));
|
||||
if (arresi.scriptId == esi.scriptId &&
|
||||
arresi.exerciseId == esi.exerciseId)
|
||||
{
|
||||
arresi.jsonPath = esi.jsonPath;
|
||||
arr[i] = esi.toJson();
|
||||
exported = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!exported)
|
||||
{
|
||||
DEBUG_LOG(m_logger) << "info scriptId=" << esi.scriptId
|
||||
<< " Name=" << esi.scriptName
|
||||
<< " Exercise=" << esi.exerciseName;
|
||||
arr.append(esi.toJson());
|
||||
}
|
||||
|
||||
obj.insert(keyArray, arr);
|
||||
auto content = QJsonDocument(obj).toJson();
|
||||
|
||||
DEBUG_LOG(m_logger) << "content =" << content;
|
||||
|
||||
file.resize(0);
|
||||
file.write(content);
|
||||
file.close();
|
||||
}
|
||||
|
||||
|
||||
bool ExportedScriptsModel::setLocalState(const int row, const bool localHost)
|
||||
{
|
||||
ExportedScriptInformation data = getRowData(row);
|
||||
|
||||
data.local = localHost;
|
||||
//data.exportTime = exportTime;
|
||||
|
||||
DEBUG_LOG(m_logger) << "setLocalState row=" << row
|
||||
<< " scriptId=" << data.scriptId
|
||||
<< " data.scriptName=" << data.scriptName
|
||||
<< " Exercise=" << data.exerciseName;
|
||||
|
||||
bool result = setRowData(row, data);
|
||||
if (result && localHost && m_rootDirectoryToScan.contains(QStringLiteral("Download")))
|
||||
{
|
||||
data.rootDirectory = m_rootDirectoryToScan;
|
||||
setRowData(row, data); //again for new download path mabe not needed
|
||||
addDownloadData(data);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool ExportedScriptsModel::setUploadState(const int row,
|
||||
const UploadState uploadState,
|
||||
const qint64 uploadTime,
|
||||
const qint64 fileSize)
|
||||
{
|
||||
ExportedScriptInformation data = getRowData(row);
|
||||
data.uploaded = uploadState;
|
||||
data.uploadTime = uploadTime;
|
||||
data.fileSize = fileSize;
|
||||
return setRowData(row, data);
|
||||
}
|
||||
|
||||
bool ExportedScriptsModel::removeRow(int row)
|
||||
{
|
||||
ExportedScriptInformation data = getRowData(row);
|
||||
|
||||
if (!LtsWidgets::LtsAbstractTableModel<ExportedScriptInformation>::removeRow(row))
|
||||
return false;
|
||||
|
||||
QString baseDir = exercisePath(data.rootDirectory, data.exerciseId);
|
||||
QString filename = baseDir + QStringLiteral("data.json");
|
||||
DEBUG_LOG(m_logger) << "filename= '" << filename << "'";
|
||||
QFile file(baseDir + QStringLiteral("data.json"));
|
||||
|
||||
if (!file.open(QIODevice::ReadWrite))
|
||||
{
|
||||
DEBUG_LOG(m_logger) << "!file.open '" << filename << "'"
|
||||
<< " no local scripts";
|
||||
return true;
|
||||
}
|
||||
|
||||
const QJsonDocument doc(QJsonDocument::fromJson(file.readAll()));
|
||||
QJsonObject obj = doc.object();
|
||||
const int exId = obj.value(QStringLiteral("exerciseId")).toInt();
|
||||
QJsonArray arr;
|
||||
|
||||
for (const auto &esi : qAsConst(m_vecData))
|
||||
{
|
||||
if (esi.exerciseId == exId)
|
||||
arr += esi.toJson();
|
||||
}
|
||||
|
||||
obj[QStringLiteral("presentations")] = arr;
|
||||
file.resize(0);
|
||||
auto content = QJsonDocument(obj).toJson();
|
||||
DEBUG_LOG(m_logger) << "content =" << content;
|
||||
|
||||
return file.write(QJsonDocument(obj).toJson());
|
||||
}
|
||||
|
||||
QString ExportedScriptsModel::exercisePath(const QString &rootDirectory, const int exerciseId) const
|
||||
{
|
||||
QString path = QStringLiteral("%1/Exercise_%2/").arg(rootDirectory).arg(exerciseId);
|
||||
#ifndef WIN32
|
||||
path.prepend(QStringLiteral("/"));
|
||||
#endif
|
||||
return path;
|
||||
}
|
||||
|
||||
//
|
||||
// ExportedScriptInformation
|
||||
//
|
||||
QJsonValue ExportedScriptInformation::toJson() const
|
||||
{
|
||||
return QJsonObject({
|
||||
{ QStringLiteral("displayName"), scriptName },
|
||||
{ QStringLiteral("path"), jsonPath },
|
||||
{ QStringLiteral("scriptId"), scriptId },
|
||||
{ QStringLiteral("exportTime"), exportTime },
|
||||
{ QStringLiteral("fileSize"), fileSize },
|
||||
{ QStringLiteral("uploadTime"), uploadTime },
|
||||
{ QStringLiteral("exerciseName"), exerciseName },
|
||||
{ QStringLiteral("exerciseId"), exerciseId }
|
||||
});
|
||||
}
|
||||
|
||||
ExportedScriptInformation ExportedScriptInformation::fromJson(const QJsonValue &val,
|
||||
const bool local,
|
||||
const UploadState uploadState)
|
||||
{
|
||||
const QJsonObject obj = val.toObject();
|
||||
|
||||
ExportedScriptInformation esi;
|
||||
esi.local = local;
|
||||
esi.scriptName = obj[QStringLiteral("displayName")].toString();
|
||||
if (esi.scriptName.isEmpty())
|
||||
esi.scriptName = obj[QStringLiteral("name")].toString();
|
||||
esi.jsonPath = obj[QStringLiteral("path")].toString();
|
||||
esi.scriptId = obj[QStringLiteral("scriptId")].toInt();
|
||||
esi.exportTime = qRound64(obj[QStringLiteral("exportTime")].toDouble());
|
||||
esi.fileSize = obj[QStringLiteral("fileSize")].toInt();
|
||||
esi.uploaded = uploadState;
|
||||
esi.uploadTime = qRound64(obj[QStringLiteral("uploadTime")].toDouble());
|
||||
esi.exerciseName = obj[QStringLiteral("exerciseName")].toString();
|
||||
esi.exerciseId = obj[QStringLiteral("exerciseId")].toInt();
|
||||
|
||||
Q_ASSERT(esi.scriptId != -1);
|
||||
Q_ASSERT(esi.exerciseId != -1);
|
||||
|
||||
if (esi.scriptId != -1 && esi.exerciseId != -1)
|
||||
esi.valid = true;
|
||||
|
||||
return esi;
|
||||
}
|
||||
|
||||
} // namespace TakeHomePackageManager
|
||||
+99
@@ -0,0 +1,99 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
* Show the exported scripts and allow them to upload them to the server
|
||||
* $Id: tpm_exported_scripts_model.h 53111 2021-03-22 08:21:29Z wille_m $
|
||||
* $HeadURL: svn://slssvn/TDS/branches/product/iterations/21_03_salted_caramel/Lts/Plugins/TakeHomePackageManager/tpm_exported_scripts_model.h $
|
||||
* $Author: wille_m $
|
||||
* Created by Gerlinde Rudolphus, NLE, 2019-03-25
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <logger.h>
|
||||
|
||||
#include <LtsWidgets/lts_abstract_table_model.h>
|
||||
#include <Plugins/ExerciseManagement/exm_data_plugin_interface.h>
|
||||
#include <Plugins/AssetManagement/asm_data_plugin_interface.h>
|
||||
#include <Common/thpcommon_export.h>
|
||||
|
||||
|
||||
namespace TakeHomePackageManager
|
||||
{
|
||||
|
||||
enum UploadState
|
||||
{
|
||||
ThpUnknownUploadState,
|
||||
ThpUploadNotActive,
|
||||
ThpConnectToHost,
|
||||
ThpUploadActive,
|
||||
ThpAlreadyUploaded,
|
||||
ThpUploadError,
|
||||
};
|
||||
|
||||
struct ExportedScriptInformation
|
||||
{
|
||||
bool valid { false };
|
||||
|
||||
QString exerciseName;
|
||||
QString scriptName;
|
||||
QString jsonPath;
|
||||
QString rootDirectory; // temp
|
||||
qint64 exerciseId = -1;
|
||||
qint64 exportTime = 0;
|
||||
qint64 fileSize = 0;
|
||||
bool local = true; // On local Host
|
||||
UploadState uploaded = ThpUnknownUploadState;
|
||||
qint64 uploadTime = 0;
|
||||
int scriptId = -1;
|
||||
|
||||
int id() const { return scriptId; }
|
||||
|
||||
QJsonValue toJson() const;
|
||||
static ExportedScriptInformation fromJson(const QJsonValue &val,
|
||||
const bool local = true,
|
||||
const UploadState uploadStates = ThpUnknownUploadState);
|
||||
};
|
||||
|
||||
|
||||
|
||||
class THPCOMMON_EXPORT ExportedScriptsModel : public LtsWidgets::LtsAbstractTableModel<ExportedScriptInformation>
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
enum Columns
|
||||
{
|
||||
ColId,
|
||||
ColExercise,
|
||||
ColName,
|
||||
ColCreated,
|
||||
ColDownloaded,
|
||||
ColLocation,
|
||||
ColUploadedTime,
|
||||
ColUploaded,
|
||||
};
|
||||
explicit ExportedScriptsModel(QObject *parent = nullptr);
|
||||
|
||||
bool init(const QString &rootDirectoryToScan);
|
||||
void setServerContent(const QString &jsonArray);
|
||||
void setOC(bool isOC) { m_isOC = isOC; }
|
||||
|
||||
|
||||
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
|
||||
bool removeRow(int row) override;
|
||||
bool setLocalState(const int row, const bool localHost);
|
||||
bool setUploadState(const int row, const UploadState uploadState, const qint64 uploadTime, const qint64 fileSize);
|
||||
bool createDataJson(const QString &rootDirectory, const std::shared_ptr<ExerciseManagement::Interface::Dtc> &exmDtc,
|
||||
const std::shared_ptr<AssetManagement::Interface::Dtc> &asmDtc,
|
||||
int exerciseId) const;
|
||||
void addDownloadData(const ExportedScriptInformation &esi);
|
||||
|
||||
private:
|
||||
QString exercisePath(const QString &rootDirectory, const int exerciseId) const;
|
||||
qint64 downloadTime(const ExportedScriptInformation &val) const;
|
||||
|
||||
private:
|
||||
bool m_isOC { false };
|
||||
QString m_rootDirectoryToScan;
|
||||
NGL::INGLLogger &m_logger;
|
||||
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
#include "tpm_process_dialog.h"
|
||||
#include "ui_tpm_process_dialog.h"
|
||||
|
||||
|
||||
namespace TakeHomePackageManager
|
||||
{
|
||||
ProcessOverlay::ProcessOverlay(QWidget *parent)
|
||||
: QWidget(parent)
|
||||
, m_ui(new Ui::ProcessOverlay)
|
||||
{
|
||||
m_ui->setupUi(this);
|
||||
|
||||
connect(m_ui->pbCancel, &QPushButton::clicked,
|
||||
this, [this]()
|
||||
{
|
||||
m_ui->pbCancel->setEnabled(false);
|
||||
emit signalCancel();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
ProcessOverlay::~ProcessOverlay()
|
||||
{
|
||||
delete m_ui;
|
||||
}
|
||||
|
||||
|
||||
void ProcessOverlay::setCancelEnabled(bool enabled)
|
||||
{
|
||||
m_ui->pbCancel->setEnabled(enabled);
|
||||
}
|
||||
|
||||
|
||||
void ProcessOverlay::setStatus(const QString &text, qint64 totalFiles, qint64 curFile, qint64 totalSize, qint64 curPos, const QString &curName)
|
||||
{
|
||||
if (!text.isEmpty()) // only set new text if not empty (we dont want to send the text on every update..
|
||||
m_ui->lblStatus->setText(text);
|
||||
|
||||
if (!curName.isEmpty()) // only set new text if not empty (we dont want to send the text on every update..
|
||||
m_ui->lblFilename->setText(curName);
|
||||
|
||||
double progressBytes = (totalSize == 0) ? (double)0 : ((double)curPos / (double)totalSize);
|
||||
double progressFiles = (((double)curFile - 1) / (double)totalFiles);
|
||||
double progressTotal = progressFiles + (progressBytes / (double)totalFiles);
|
||||
|
||||
progressBytes = qRound(progressBytes * 100);
|
||||
progressFiles = qRound(progressFiles * 100);
|
||||
progressTotal = qRound(progressTotal * 100);
|
||||
|
||||
m_ui->pbarFiles->setValue(progressFiles);
|
||||
m_ui->pbarBytes->setValue(progressBytes);
|
||||
m_ui->pbarProgress->setValue(progressTotal);
|
||||
|
||||
QLocale l;
|
||||
|
||||
if (curFile == 0)
|
||||
m_ui->lblFiles->setText(QStringLiteral(" - "));
|
||||
else
|
||||
m_ui->lblFiles->setText(QStringLiteral("%1 / %2").arg(l.toString(curFile)).arg(l.toString(totalFiles)));
|
||||
|
||||
if (curPos == 0)
|
||||
m_ui->lblBytes->setText(QStringLiteral(" - "));
|
||||
else
|
||||
m_ui->lblBytes->setText(QStringLiteral("%1 / %2").arg(l.toString(curPos)).arg(l.toString(totalSize)));
|
||||
}
|
||||
} // namespace TakehomePackageManager
|
||||
@@ -0,0 +1,32 @@
|
||||
#pragma once
|
||||
|
||||
#include <Common/thpcommon_export.h>
|
||||
|
||||
#include <QWidget>
|
||||
|
||||
|
||||
namespace Ui
|
||||
{
|
||||
class ProcessOverlay;
|
||||
}
|
||||
|
||||
namespace TakeHomePackageManager
|
||||
{
|
||||
class THPCOMMON_EXPORT ProcessOverlay : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
ProcessOverlay(QWidget *parent = nullptr);
|
||||
~ProcessOverlay();
|
||||
|
||||
void setStatus(const QString &text = QString(), qint64 totalFiles = 1, qint64 curFile = 1, qint64 totalSize = 0, qint64 curPos = 0, const QString &curName = QString());
|
||||
void setCancelEnabled(bool enabled = true);
|
||||
|
||||
signals:
|
||||
void signalCancel();
|
||||
|
||||
private:
|
||||
Ui::ProcessOverlay *m_ui;
|
||||
bool m_decompressing { false };
|
||||
};
|
||||
} // namespace TakehomePackageManager
|
||||
@@ -0,0 +1,174 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>ProcessOverlay</class>
|
||||
<widget class="QWidget" name="ProcessOverlay">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>700</width>
|
||||
<height>220</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Maximum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>700</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string notr="true">Form</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="3" column="1">
|
||||
<widget class="QLabel" name="lblBytes">
|
||||
<property name="text">
|
||||
<string notr="true"> - </string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="0">
|
||||
<widget class="QLabel" name="label_5">
|
||||
<property name="text">
|
||||
<string>Progress</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="2">
|
||||
<widget class="QProgressBar" name="pbarFiles">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="value">
|
||||
<number>0</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="1">
|
||||
<widget class="QLabel" name="lblFiles">
|
||||
<property name="text">
|
||||
<string notr="true"> - </string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="lblStatus">
|
||||
<property name="text">
|
||||
<string notr="true">Preparing ...</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="0">
|
||||
<widget class="QLabel" name="label_4">
|
||||
<property name="text">
|
||||
<string>Files</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="2">
|
||||
<widget class="QProgressBar" name="pbarBytes">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="value">
|
||||
<number>0</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="7" column="2">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QPushButton" name="pbCancel">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Cancel</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="6" column="0" colspan="3">
|
||||
<widget class="Line" name="line">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QLabel" name="lblFilename">
|
||||
<property name="text">
|
||||
<string notr="true"> - </string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="text">
|
||||
<string>Bytes</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="1" colspan="2">
|
||||
<widget class="QProgressBar" name="pbarProgress">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="value">
|
||||
<number>0</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="text">
|
||||
<string>Filename</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0" colspan="3">
|
||||
<widget class="Line" name="line_2">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
Reference in new issue
Block a user