/** * 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 #include "pdf_downloader.h" #include #include #include #include #include #include #include #include #include "main_handler.h" #include "settings.h" #include #include 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 &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 ¤tInfo : 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 &cont, int idToSearch) -> bool { for (const int curId : cont) if (curId == idToSearch) return true; return false; }; QVector pdfInfoVec; for (const auto ¤t : 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) <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>(new NGLDB::DbServiceHandler()); 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: "< query = std::unique_ptr(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 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(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