Files
qt5-legatus-legacy/Lts/Services/OcBaseDownload/handler_pdf_download.cpp
T
2026-08-13 18:33:10 +02:00

489 lines
16 KiB
C++
Executable File

/**
* Handler for PDF download coordination
* $Id$
* $HeadURL$
* $Author$
* Created by Hannes Fritsche, NLE, 26-04-2019
*/
#include "handler_pdf_download.h"
#include "WebSocketInterface/Messages/reply_message.h"
#include <QUuid>
#include "pdf_downloader.h"
#include <ngldb/db_service_handler.h>
#include <nglsettings/nglsettings_connections.h>
#include <nglsettings/nglsettings_settings.h>
#include <ngldb/dbtransaction.h>
#include <ngldb/dbconnection.h>
#include <ngldb/ngldb_export.h>
#include <ngldb/nsqlquery.h>
#include <ngltools/ngltools_filesystem.h>
#include "main_handler.h"
#include "settings.h"
#include <QFileInfo>
#include <QDir>
namespace OcBaseDownload
{
HandlerPdfDownload::HandlerPdfDownload(MainHandler *mainHandler, QObject *parent)
: QObject(parent)
, Interface::Fts::GetPdfListRequestHandler(mainHandler->dds()->fts())
, Interface::Fts::StartPdfDownloadRequestHandler(mainHandler->dds()->fts())
, Interface::Fts::OcPdfDownloadRequestHandler(mainHandler->dds()->fts())
, m_fts(mainHandler->dds()->fts())
, m_webSocket(mainHandler->handlerWebSocket())
, m_mainHandler(mainHandler)
, m_exerciseId(0)
, m_hostname(NGL::Connections::hostName(NGL::Connections::eConnection::SCM_WEBSOCKET))
, m_remoteHostName(Settings::remoteDbHostName())
, m_currentPort(NGL::Connections::port(NGL::Connections::eConnection::SCM_WEBSOCKET))
, m_currentMode(Mode::PORT_REQUEST)
, m_downloader(nullptr)
, m_logger(getLogger("HandlerPdfDownload"))
{
}
HandlerPdfDownload::~HandlerPdfDownload()
{
m_fts = nullptr;
}
bool HandlerPdfDownload::init()
{
INFO_LOG(m_logger) << "HandlerPdfDownload initialised!";
return true;
}
void HandlerPdfDownload::handleServicePortsInformation(const QVector<WebSocketInterface::Messages::ServicePortsInformation::ExerciseInformation> &portsInformation)
{
m_webSocket->close();
int flmPort = 0;
for (const auto &exercise : portsInformation)
{
if (exercise.exerciseId == 0)
{
for (const auto &service : exercise.services)
{
if (service.abbreviation == QStringLiteral("flm"))
{
for (const auto &port : service.ports)
{
if (port.type == Core::PortType::WebSocketServer)
{
flmPort = port.number;
break;
}
}
}
}
}
}
m_currentMode = Mode::LIST_REQUEST;
if (flmPort)
m_webSocket->open(m_hostname, flmPort);
else
{
ERROR_LOG(m_logger) << "Could not connect to FileManagement WebSocket server.";
m_currentMode = Mode::PORT_REQUEST;
}
}
void HandlerPdfDownload::handlePdfListData(const WebSocketInterface::Messages::PdfListMessage::PdfListData &pdfListData)
{
m_webSocket->close();
PdfListReply rep;
auto &documentInformation = rep.documents();
documentInformation.reserve(pdfListData.pdfData.size());
for (const WebSocketInterface::Messages::PdfListMessage::PdfInformation &currentInfo : pdfListData.pdfData)
documentInformation.emplace_back(currentInfo.id,
currentInfo.name.toStdString(),
false,
false);
disconnect(m_webSocket.get(), &WebSocketInterface::WebSocketInterfaceHandler::connected, this, &HandlerPdfDownload::onConnected);
disconnect(m_webSocket.get(), &WebSocketInterface::WebSocketInterfaceHandler::disconnected, this, &HandlerPdfDownload::onDisconnected);
m_fts->getPdfListReply(m_pdfListCookie, rep);
m_pdfData = pdfListData.pdfData;
m_currentMode = Mode::PORT_REQUEST;
m_webSocket->setCurrentDownload(WebSocketInterface::CurrentDownload::NOTHING);
}
void HandlerPdfDownload::onGetPdfListRequest(const rti::core::SampleIdentity &cookie, const GetPdfListRequest &request)
{
m_webSocket->setCurrentDownload(WebSocketInterface::CurrentDownload::PDF);
m_hostname = NGL::Connections::hostName(NGL::Connections::eConnection::SCM_WEBSOCKET);
m_currentPort = NGL::Connections::port(NGL::Connections::eConnection::SCM_WEBSOCKET);
m_pdfListCookie = cookie;
m_hostname = QString::fromStdString(request.hostname());
m_currentPort = request.portNumber();
m_exerciseId = request.exerciseId();
connect(m_webSocket.get(), &WebSocketInterface::WebSocketInterfaceHandler::connected, this, &HandlerPdfDownload::onConnected);
connect(m_webSocket.get(), &WebSocketInterface::WebSocketInterfaceHandler::disconnected, this, &HandlerPdfDownload::onDisconnected);
openWebSocket();
}
void HandlerPdfDownload::onStartPdfDownloadRequest(const rti::core::SampleIdentity &cookie, const StartPdfDownloadRequest &request)
{
ERROR_LOG(m_logger) <<" onOcPdfDownloadRequest START" ;
m_downloadReply = PdfDownloadReply();
m_downloadCookie = cookie;
if (m_logger.isDebugEnabled())
{
QString str = QStringLiteral("Got PDF download request for ids: ");
for (int32_t id : request.documentIds())
str.append(QString::number(id) + QStringLiteral(" "));
DEBUG_LOG(m_logger) << str;
}
if (m_hostname.isEmpty())
{
QString errorStr = QStringLiteral("Not able to start PDF download. Hostname not set.");
ERROR_LOG(m_logger) << errorStr;
m_downloadReply.errorString(errorStr.toStdString());
m_fts->startPdfDownloadReply(m_downloadCookie, m_downloadReply);
return;
}
typedef WebSocketInterface::Messages::PdfListMessage::PdfInformation pdfInfo;
const auto containsId = [](const std::vector<int> &cont, int idToSearch) -> bool
{
for (const int curId : cont)
if (curId == idToSearch)
return true;
return false;
};
QVector<pdfInfo> pdfInfoVec;
for (const auto &current : qAsConst(m_pdfData))
{
if (!containsId(request.documentIds(), current.id))
continue;
pdfInfoVec.append(current);
}
if (m_downloader)
{
disconnect(m_downloader);
delete m_downloader;
m_downloader = nullptr;
}
m_downloader = new PdfDownloader(pdfInfoVec, m_mainHandler, m_hostname, m_exerciseId, this);
connect(m_downloader, &PdfDownloader::newState,
this, &HandlerPdfDownload::onNewDownloadState);
connect(m_downloader, &PdfDownloader::allDownloadsFinished,
this, &HandlerPdfDownload::onAllDownloadsFinished);
m_downloader->startDownload();
}
void HandlerPdfDownload::onOcPdfDownloadRequest(const rti::core::SampleIdentity &cookie, const OcPdfDownloadRequest &request)
{
DEBUG_LOG(m_logger) <<LOGFUNCTION<<" START OcPdfDownloadRequest" ;
m_ocPdfDownloadCookie = cookie;
m_ocPdfDownloadReply = Core::FunctionReply();
if(openRemoteDbConnection(QStringLiteral("lts_ets_pro")) == false)
{
QString errorStr = QStringLiteral("Not able to open remote connection to DB");
ERROR_LOG(m_logger) << errorStr;
m_ocPdfDownloadReply.errorString(errorStr.toStdString());
m_fts->ocPdfDownloadReply(m_ocPdfDownloadCookie, m_ocPdfDownloadReply);
return;
}
DEBUG_LOG(m_logger) << "Got PDF download request for ids: "<< request.folderId();
m_folderId = request.folderId();
if(m_folderId == -1)
{
QString errorStr = QStringLiteral("Not able to start PDF download. Folder id not valid.");
ERROR_LOG(m_logger) << errorStr;
m_ocPdfDownloadReply.errorString(errorStr.toStdString());
m_fts->ocPdfDownloadReply(m_ocPdfDownloadCookie, m_ocPdfDownloadReply);
return;
}
m_operatorName = QString::fromStdString(request.matrixOperator());
if (m_hostname.isEmpty())
{
QString errorStr = QStringLiteral("Not able to start PDF download. Hostname not set.");
ERROR_LOG(m_logger) << errorStr;
m_ocPdfDownloadReply.errorString(errorStr.toStdString());
m_fts->ocPdfDownloadReply(m_ocPdfDownloadCookie, m_ocPdfDownloadReply);
return;
}
exportOcPdfFileContent();
m_fts->ocPdfDownloadReply(m_ocPdfDownloadCookie, m_ocPdfDownloadReply);
closeDb(m_remoteDbConnection);
}
void HandlerPdfDownload::exportOcPdfFileContent()
{
// handling FLM data
using namespace FileManagement;
auto remoteDb = std::unique_ptr<NGLDB::DbServiceHandler<DatabaseInterface>>(new NGLDB::DbServiceHandler<DatabaseInterface>());
if (!remoteDb->init(0, m_remoteDbConnection))
{
const QString err = tr("Can't open remote Database");
m_ocPdfDownloadReply.errorString(err.toStdString());
return;
}
auto remoteDbIfc = remoteDb->db();
const QString dir = QStringLiteral("%1/OcPdf/%2/").arg(QDir::tempPath()).arg(m_operatorName);
DEBUG_LOG(m_logger)<<" export path: "<<dir;
QDir directory(dir);
if(!directory.exists())
directory.mkpath(dir);
const QString fExts = QStringLiteral(".pdf");
//first we look up our username folder
QString strm = QStringLiteral("Select id from flm.flm_folder WHERE name = '" ) + m_operatorName + QStringLiteral("' and '") +
QString::number(m_folderId) + QStringLiteral("' = ANY(path) LIMIT 1;");
DEBUG_LOG(m_logger) << "folder content ID querry" << strm;
std::unique_ptr<NSqlQuery> query = std::unique_ptr<NSqlQuery>(new NSqlQuery(remoteDbIfc->database(), m_logger));
query->prepare(strm);
qlonglong folderId = -1;
if (query->exec())
{
while (query->next())
{
folderId = query->value(0).toLongLong();
}
}
if(folderId < 0)
{
const QString err = tr("Can't find user folder in remote Database");
m_ocPdfDownloadReply.errorString(err.toStdString());
return;
}
/// check first if the pdf file is not alreay exist and oudated without loading file content
/// get ids of pdf file matching the named condition
QString where = QStringLiteral(" WHERE folder_id =%1 ").arg(QString::number(folderId)) +
QStringLiteral(" AND file_type_id=%1 ").arg(FileManagement::FileType_def::Pdf);
QString tableName(QStringLiteral("flm.flm_file_content"));
QString stmt(QStringLiteral("SELECT id, name, last_modified "
" FROM ") + tableName + where);
QStringList idsList;
query->prepare(stmt);
if (query->exec())
{
Database::FlmFileContent data;
while (query->next())
{
int i = 0;
data.id = query->value(i++).toLongLong();
data.name = query->value(i++).toString();
data.lastModified = query->value(i++).toLongLong();
QString fileName = dir + QString(data.name).trimmed() + fExts;
QFileInfo fileInfo(fileName);
bool fileExists = fileInfo.exists();
if(!fileExists ||
(data.lastModified > fileInfo.lastModified().toMSecsSinceEpoch()))
{
idsList << QString::number(data.id);
}
}
}
if(idsList.isEmpty())
{
WARNING_LOG(m_logger)<< " pdf files outdated! ";
return;
}
/// start a new query with id matching the define condition of beeing newer than current existing file
/// or file doesn't exist
/// folderId is used in the first statement so no need to reuse it for second one. just use id offlm_file_content
where = QStringLiteral(" WHERE id IN (%1)").arg(idsList.join(QStringLiteral(","))) +
QStringLiteral(" AND file_type_id=%1 ").arg(FileManagement::FileType_def::Pdf);
tableName = (QStringLiteral("flm.flm_file_content"));
stmt = (QStringLiteral("SELECT id, name, file_status_id, content, last_modified FROM ") + tableName + where);
DEBUG_LOG(m_logger) << "### statement: " << stmt;
query->prepare(stmt);
QVector<Database::FlmFileContent> dataVec;
if (query->exec())
{
Database::FlmFileContent data;
while (query->next())
{
int i = 0;
data.id = query->value(i++).toLongLong();
data.name = query->value(i++).toString();
data.fileStatusId = static_cast<FileManagement::FileStatus_def::type>(query->value(i++).toInt());
data.content = query->value(i++).toByteArray();
data.lastModified = query->value(i++).toLongLong();
if(data.fileStatusId == FileManagement::FileStatus::Deleted)
{
DEBUG_LOG(m_logger)<< " skip ";
continue;
}
dataVec.append(data);
}
}
else
{
const QString errorStr = tr("Not able to export PDF.");
ERROR_LOG(m_logger) << errorStr;
m_ocPdfDownloadReply.errorString(errorStr.toStdString());
return;
}
for(const auto &data : dataVec)
{
QByteArray content = data.content;
QString fileName = dir + QString(data.name).trimmed() + fExts;
QFile file(fileName);
QFileInfo fileInfo(file);
// remove if exist file by their name
if(fileInfo.exists() && file.remove() == false)
{
QString err = QStringLiteral("Couldn't replace existing file: ") + data.name;
WARNING_LOG(m_logger)<< err;
continue;
}
if(!file.open(QIODevice::WriteOnly))
{
QString err = tr("Failed to write file: ") + (data.name);
m_ocPdfDownloadReply.errorString(err.toStdString());
return;
}
file.write(content);
file.close();
}
}
bool HandlerPdfDownload::openRemoteDbConnection(const QString &remoteDatabaseName)
{
DEBUG_LOG(m_logger) <<" Open RemoteDb Connection..." ;
if (m_remoteDbConnection.isValid())
closeDb(m_remoteDbConnection);
bool bOk;
m_remoteDbConnection = openDb(remoteDatabaseName, true, &bOk);
if (!bOk)
return false;
return true;
}
QSqlDatabase HandlerPdfDownload::openDb(const QString &databaseName, bool remote, bool *bOk)
{
if (bOk)
*bOk = true;
// open postgres db, read table structure, read static data
auto s = Settings::settings();
const QString dbUser(QStringLiteral("ets_exm"));
const QString dbPasswd(QStringLiteral("ets11de"));
QSqlDatabase dbConnection = DBConnection::open(m_logger,
dbUser,
dbPasswd,
s->databaseDriver(),
remote ? m_remoteHostName : s->databaseHostname(),
remote ? Settings::remoteDbName() : databaseName);
DEBUG_LOG(m_logger) << "Trying to open database " << s->databaseHostname() << ":" << databaseName;
if (!dbConnection.isOpen())
{
ERROR_LOG(m_logger) << "Error opening database " << s->databaseHostname() << ":" << databaseName;
if (bOk)
*bOk = false;
}
else
NOTICE_LOG(m_logger) << "Opened database connection " << dbConnection.connectionName();
return dbConnection;
}
void HandlerPdfDownload::closeDb(QSqlDatabase &dbConn)
{
const QString connName = dbConn.connectionName();
NOTICE_LOG(m_logger) << "Closing database connection " << connName;
dbConn.close();
dbConn = QSqlDatabase();
QSqlDatabase::removeDatabase(connName);
}
void HandlerPdfDownload::onConnected()
{
INFO_LOG(m_logger) << "Connected to WebSocket server";
switch (m_currentMode)
{
case Mode::PORT_REQUEST:
m_webSocket->send(new WebSocketInterface::Messages::ServicePortsInformationRequest(QUuid::createUuid().toString()));
break;
case Mode::LIST_REQUEST:
m_webSocket->send(new WebSocketInterface::Messages::PdfListRequest(QUuid::createUuid().toString(), m_exerciseId));
break;
}
}
void HandlerPdfDownload::onDisconnected()
{
INFO_LOG(m_logger) << "Disconnected from server.";
}
void HandlerPdfDownload::onNewDownloadState(int progress, const QString &msg)
{
DEBUG_LOG(m_logger) << msg;
m_downloadReply.replyState(Core::FtReplyState::InProgress);
m_downloadReply.progress(progress);
m_downloadReply.currentStep(msg.toStdString());
m_fts->startPdfDownloadReply(m_downloadCookie, m_downloadReply);
}
void HandlerPdfDownload::onAllDownloadsFinished()
{
DEBUG_LOG(m_logger) << "Downloads finished";
m_downloadReply.replyState(Core::FtReplyState::Completed);
m_downloadReply.currentStep(tr("Downloads finished").toStdString());
m_downloadReply.progress(100);
m_fts->startPdfDownloadReply(m_downloadCookie, m_downloadReply);
}
void HandlerPdfDownload::openWebSocket()
{
DEBUG_LOG(m_logger) << "Opening websocket to: " << m_hostname << ":" << m_currentPort;
m_webSocket->open(m_hostname, m_currentPort);
}
} // namespace OcBaseDownload