diff --git a/src/blackcore/databaseauthentication.cpp b/src/blackcore/databaseauthentication.cpp deleted file mode 100644 index 60836bae1..000000000 --- a/src/blackcore/databaseauthentication.cpp +++ /dev/null @@ -1,152 +0,0 @@ -/* Copyright (C) 2015 - * swift project Community / Contributors - * - * This file is part of swift project. It is subject to the license terms in the LICENSE file found in the top-level - * directory of this distribution and at http://www.swift-project.org/license.html. No part of swift project, - * including this file, may be copied, modified, propagated, or distributed except according to the terms - * contained in the LICENSE file. - */ - -#include "blackcore/application.h" -#include "blackcore/data/globalsetup.h" -#include "blackcore/databaseauthentication.h" -#include "blackmisc/json.h" -#include "blackmisc/logcategory.h" -#include "blackmisc/logcategorylist.h" -#include "blackmisc/logmessage.h" -#include "blackmisc/network/authenticateduser.h" -#include "blackmisc/network/networkutils.h" -#include "blackmisc/network/rolelist.h" -#include "blackmisc/network/url.h" -#include "blackmisc/statusmessage.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -using namespace BlackMisc; -using namespace BlackMisc::Network; - -namespace BlackCore -{ - CDatabaseAuthenticationService::CDatabaseAuthenticationService(QObject *parent) : - QObject(parent) - { - // void - } - - void CDatabaseAuthenticationService::gracefulShutdown() - { - if (this->m_shutdown) { return; } - this->m_shutdown = true; - this->logoff(); - } - - CStatusMessageList CDatabaseAuthenticationService::login(const QString &username, const QString &password) - { - CStatusMessageList msgs; - static const CLogCategoryList cats(CLogCategoryList(this).join({ CLogCategory::validation()})); - - if (this->m_shutdown) { msgs.push_back(CStatusMessage(cats, CStatusMessage::SeverityError, "Shutdown in progress")); return msgs; } - - QString un(username.trimmed()); - QString pw(password.trimmed()); - if (un.isEmpty()) { msgs.push_back(CStatusMessage(cats, CStatusMessage::SeverityError, "No user name/id")); } - if (pw.isEmpty()) { msgs.push_back(CStatusMessage(cats, CStatusMessage::SeverityError, "No password")); } - if (!msgs.isEmpty()) { return msgs; } - - const CUrl url(sApp->getGlobalSetup().getDbLoginServiceUrl()); - QString msg; - if (!CNetworkUtils::canConnect(url, msg)) - { - msgs.push_back(CStatusMessage(cats, CStatusMessage::SeverityError, msg)); - return msgs; - } - - QUrlQuery params; - params.addQueryItem("username", un); - params.addQueryItem("password", pw); - if (sApp->getGlobalSetup().dbDebugFlag()) { CNetworkUtils::addDebugFlag(params); } - - QString query = params.toString(); - const QNetworkRequest request(CNetworkUtils::getNetworkRequest(url, CNetworkUtils::PostUrlEncoded)); - sApp->postToNetwork(request, query.toUtf8(), { this, &CDatabaseAuthenticationService::ps_parseServerResponse}); - QString rm("Sent request to authentication server %1"); - msgs.push_back(CStatusMessage(cats, CStatusMessage::SeverityInfo, rm.arg(url.toQString()))); - return msgs; - } - - void CDatabaseAuthenticationService::logoff() - { - CUrl url(sApp->getGlobalSetup().getDbLoginServiceUrl()); - url.setQuery("logoff=true"); - QNetworkRequest request(CNetworkUtils::getNetworkRequest(url)); - sApp->getFromNetwork(request, { this, &CDatabaseAuthenticationService::ps_parseServerResponse }); - this->m_swiftDbUser.set(CAuthenticatedUser()); - } - - void CDatabaseAuthenticationService::ps_parseServerResponse(QNetworkReply *nwReplyPtr) - { - // always cleanup reply - QScopedPointer nwReply(nwReplyPtr); - - if (this->m_shutdown) { return; } - QString urlString(nwReply->url().toString()); - if (urlString.toLower().contains("logoff")) - { - sApp->deleteAllCookies(); - emit logoffFinished(); - return; - } - - if (nwReply->error() == QNetworkReply::NoError) - { - QString json(nwReply->readAll()); - if (json.isEmpty()) - { - CLogMessage(this).error("Authentication failed, no response from %1") << urlString; - return; - } - QJsonObject jsonObj(Json::jsonObjectFromString(json)); - CAuthenticatedUser user(CAuthenticatedUser::fromDatabaseJson(jsonObj)); - - CStatusMessageList msgs; - static const CLogCategoryList cats(CLogCategoryList(this).join({ CLogCategory::validation()})); - - if (!user.isAuthenticated() || !user.isValid()) - { - msgs.push_back(CStatusMessage(cats, CStatusMessage::SeverityError, "Cannot login, user or password wrong")); - } - else - { - if (!user.isEnabled()) - { - msgs.push_back(CStatusMessage(cats, CStatusMessage::SeverityError, "User is disabled")); - } - if (user.getRoles().isEmpty()) - { - msgs.push_back(CStatusMessage(cats, CStatusMessage::SeverityError, "User has no roles")); - } - } - this->m_swiftDbUser.set(user); - emit userAuthenticationFinished(user, msgs); - } - else - { - CLogMessage(this).error("Authentication failed, %1") << nwReply->errorString(); - return; - } - } - - void CDatabaseAuthenticationService::ps_userChanged() - { - // code goes here - } - -} // namespace diff --git a/src/blackcore/databaseauthentication.h b/src/blackcore/databaseauthentication.h deleted file mode 100644 index 1aa2f509f..000000000 --- a/src/blackcore/databaseauthentication.h +++ /dev/null @@ -1,68 +0,0 @@ -/* Copyright (C) 2015 - * swift project Community / Contributors - * - * This file is part of swift project. It is subject to the license terms in the LICENSE file found in the top-level - * directory of this distribution and at http://www.swift-project.org/license.html. No part of swift project, - * including this file, may be copied, modified, propagated, or distributed except according to the terms - * contained in the LICENSE file. - */ - -//! \file - -#ifndef BLACKCORE_DATABASE_CDATABASEUATHENTICATIONSERVICE_H -#define BLACKCORE_DATABASE_CDATABASEUATHENTICATIONSERVICE_H - -#include "blackcore/blackcoreexport.h" -#include "blackcore/data/authenticateduser.h" -#include "blackmisc/datacache.h" -#include "blackmisc/statusmessagelist.h" - -#include -#include - -class QNetworkReply; - -namespace BlackMisc { namespace Network { class CAuthenticatedUser; } } - -namespace BlackCore -{ - //! Databse user used with swift DB. Features role and cookie handling. - class BLACKCORE_EXPORT CDatabaseAuthenticationService: public QObject - { - Q_OBJECT - - public: - //! Constructor - CDatabaseAuthenticationService(QObject *parent = nullptr); - - //! Shutdown - void gracefulShutdown(); - - public slots: - //! Try to login to authentication web service - BlackMisc::CStatusMessageList login(const QString &id, const QString &password); - - //! Logoff - void logoff(); - - signals: - //! User authenticated - void userAuthenticationFinished(const BlackMisc::Network::CAuthenticatedUser &user, const BlackMisc::CStatusMessageList &loginStatus); - - //! Logoff completed - void logoffFinished(); - - private slots: - //! Parse login answer - void ps_parseServerResponse(QNetworkReply *nwReplyPtr); - - //! User object changed - void ps_userChanged(); - - private: - BlackMisc::CData m_swiftDbUser {this, &CDatabaseAuthenticationService::ps_userChanged}; - bool m_shutdown = false; - }; -} // namespace - -#endif // guard diff --git a/src/blackcore/databasewriter.cpp b/src/blackcore/databasewriter.cpp deleted file mode 100644 index 2eb57360f..000000000 --- a/src/blackcore/databasewriter.cpp +++ /dev/null @@ -1,140 +0,0 @@ -/* Copyright (C) 2015 - * swift project Community / Contributors - * - * This file is part of swift project. It is subject to the license terms in the LICENSE file found in the top-level - * directory of this distribution and at http://www.swift-project.org/license.html. No part of swift project, - * including this file, may be copied, modified, propagated, or distributed except according to the terms - * contained in the LICENSE file. - */ - -#include "blackcore/application.h" -#include "blackcore/data/globalsetup.h" -#include "blackcore/databasewriter.h" -#include "blackmisc/datastoreutility.h" -#include "blackmisc/logcategory.h" -#include "blackmisc/logcategorylist.h" -#include "blackmisc/network/networkutils.h" -#include "blackmisc/statusmessage.h" - -#include -#include -#include -#include -#include -#include -#include -#include - -using namespace BlackMisc; -using namespace BlackMisc::Network; -using namespace BlackMisc::Simulation; - -namespace BlackCore -{ - CDatabaseWriter::CDatabaseWriter(const Network::CUrl &baseUrl, QObject *parent) : - QObject(parent), - m_modelPublishUrl(getModelPublishUrl(baseUrl)) - { - // void - } - - CStatusMessageList CDatabaseWriter::asyncPublishModels(const CAircraftModelList &models) - { - CStatusMessageList msgs; - if (m_shutdown) - { - msgs.push_back(CStatusMessage(CStatusMessage::SeverityWarning, "Database writer shuts down")); - return msgs; - } - - if (m_pendingReply) - { - msgs.push_back(CStatusMessage(CStatusMessage::SeverityWarning, "Another write operation in progress")); - return msgs; - } - - QUrl url(m_modelPublishUrl.toQUrl()); - QNetworkRequest request(url); - CNetworkUtils::ignoreSslVerification(request); - QHttpMultiPart *multiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType, this); - multiPart->append(CNetworkUtils::getJsonTextMultipart(models.toDatabaseJson())); - if (sApp->getGlobalSetup().dbDebugFlag()) - { - multiPart->append(CNetworkUtils::getMultipartWithDebugFlag()); - } - - m_pendingReply = sApp->postToNetwork(request, multiPart, { this, &CDatabaseWriter::ps_postResponse}); - multiPart->setParent(m_pendingReply); - return msgs; - } - - void CDatabaseWriter::gracefulShutdown() - { - m_shutdown = true; - if (m_pendingReply) - { - m_pendingReply->abort(); - m_pendingReply = nullptr; - } - } - - void CDatabaseWriter::ps_postResponse(QNetworkReply *nwReplyPtr) - { - static const CLogCategoryList cats(CLogCategoryList(this).join({ CLogCategory::swiftDbWebservice()})); - QScopedPointer nwReply(nwReplyPtr); - m_pendingReply = nullptr; - QUrl url(nwReply->url()); - QString urlString(url.toString()); - - if (m_shutdown) - { - nwReply->abort(); - return; - } - - if (nwReply->error() == QNetworkReply::NoError) - { - const QString dataFileData(nwReply->readAll().trimmed()); - nwReply->close(); // close asap - if (dataFileData.isEmpty()) - { - const CStatusMessageList msgs({CStatusMessage(cats, CStatusMessage::SeverityError, "No response data from " + urlString)}); - emit published(CAircraftModelList(), CAircraftModelList(), msgs); - return; - } - - CAircraftModelList modelsPublished; - CAircraftModelList modelsSkipped; - CStatusMessageList msgs; - bool success = CDatastoreUtility::parseSwiftPublishResponse(dataFileData, modelsPublished, modelsSkipped, msgs); - emit published(modelsPublished, modelsSkipped, msgs); - Q_UNUSED(success); - } - else - { - QString error = nwReply->errorString(); - nwReply->close(); // close asap - const CStatusMessageList msgs( {CStatusMessage(cats, CStatusMessage::SeverityError, "HTTP error: " + error)}); - emit published(CAircraftModelList(), CAircraftModelList(), msgs); - } - } - - CUrl CDatabaseWriter::getModelPublishUrl(const Network::CUrl &baseUrl) - { - return baseUrl.withAppendedPath("service/publishmodels.php"); - } - - QList CDatabaseWriter::splitData(const QByteArray &data, int size) - { - if (data.size() <= size) { return QList({data}); } - int pos = 0, arrsize = data.size(); - QList arrays; - while (pos < arrsize) - { - QByteArray arr = data.mid(pos, size); - arrays << arr; - pos += arr.size(); - } - return arrays; - } -} // namespace diff --git a/src/blackcore/databasewriter.h b/src/blackcore/databasewriter.h deleted file mode 100644 index e986236f9..000000000 --- a/src/blackcore/databasewriter.h +++ /dev/null @@ -1,65 +0,0 @@ -/* Copyright (C) 2015 - * swift project Community / Contributors - * - * This file is part of swift project. It is subject to the license terms in the LICENSE file found in the top-level - * directory of this distribution and at http://www.swift-project.org/license.html. No part of swift project, - * including this file, may be copied, modified, propagated, or distributed except according to the terms - * contained in the LICENSE file. - */ - -//! \file - -#ifndef BLACKCORE_DATABASE_WRITER_H -#define BLACKCORE_DATABASE_WRITER_H - -#include "blackcore/blackcoreexport.h" -#include "blackmisc/network/url.h" -#include "blackmisc/simulation/aircraftmodellist.h" -#include "blackmisc/statusmessagelist.h" - -#include -#include -#include - -class QNetworkReply; - -namespace BlackCore -{ - //! Write to the swift DB - class BLACKCORE_EXPORT CDatabaseWriter : public QObject - { - Q_OBJECT - - public: - //! Constructor - CDatabaseWriter(const BlackMisc::Network::CUrl &baseUrl, QObject *parent); - - //! Write model to DB - BlackMisc::CStatusMessageList asyncPublishModels(const BlackMisc::Simulation::CAircraftModelList &models); - - //! Shutdown - void gracefulShutdown(); - - signals: - //! Published models, the response to \sa asyncPublishModels - void published(const BlackMisc::Simulation::CAircraftModelList &modelsPublished, const BlackMisc::Simulation::CAircraftModelList &modelsSkipped, const BlackMisc::CStatusMessageList &messages); - - private slots: - //! Post response - void ps_postResponse(QNetworkReply *nwReplyPtr); - - private: - BlackMisc::Network::CUrl m_modelPublishUrl; - QNetworkReply *m_pendingReply = nullptr; - bool m_shutdown = false; - - //! URL model web service - static BlackMisc::Network::CUrl getModelPublishUrl(const BlackMisc::Network::CUrl &baseUrl); - - //! Split data array - static QList splitData(const QByteArray &data, int size); - - }; -} // namespace - -#endif // guard diff --git a/src/blackcore/db/databaseauthentication.cpp b/src/blackcore/db/databaseauthentication.cpp new file mode 100644 index 000000000..a5217b300 --- /dev/null +++ b/src/blackcore/db/databaseauthentication.cpp @@ -0,0 +1,154 @@ +/* Copyright (C) 2015 + * swift project Community / Contributors + * + * This file is part of swift project. It is subject to the license terms in the LICENSE file found in the top-level + * directory of this distribution and at http://www.swift-project.org/license.html. No part of swift project, + * including this file, may be copied, modified, propagated, or distributed except according to the terms + * contained in the LICENSE file. + */ + +#include "blackcore/application.h" +#include "blackcore/data/globalsetup.h" +#include "blackcore/db/databaseauthentication.h" +#include "blackmisc/json.h" +#include "blackmisc/logcategory.h" +#include "blackmisc/logcategorylist.h" +#include "blackmisc/logmessage.h" +#include "blackmisc/network/authenticateduser.h" +#include "blackmisc/network/networkutils.h" +#include "blackmisc/network/rolelist.h" +#include "blackmisc/network/url.h" +#include "blackmisc/statusmessage.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace BlackMisc; +using namespace BlackMisc::Network; + +namespace BlackCore +{ + namespace Db + { + CDatabaseAuthenticationService::CDatabaseAuthenticationService(QObject *parent) : + QObject(parent) + { + // void + } + + void CDatabaseAuthenticationService::gracefulShutdown() + { + if (this->m_shutdown) { return; } + this->m_shutdown = true; + this->logoff(); + } + + CStatusMessageList CDatabaseAuthenticationService::login(const QString &username, const QString &password) + { + CStatusMessageList msgs; + static const CLogCategoryList cats(CLogCategoryList(this).join({ CLogCategory::validation()})); + + if (this->m_shutdown) { msgs.push_back(CStatusMessage(cats, CStatusMessage::SeverityError, "Shutdown in progress")); return msgs; } + + QString un(username.trimmed()); + QString pw(password.trimmed()); + if (un.isEmpty()) { msgs.push_back(CStatusMessage(cats, CStatusMessage::SeverityError, "No user name/id")); } + if (pw.isEmpty()) { msgs.push_back(CStatusMessage(cats, CStatusMessage::SeverityError, "No password")); } + if (!msgs.isEmpty()) { return msgs; } + + const CUrl url(sApp->getGlobalSetup().getDbLoginServiceUrl()); + QString msg; + if (!CNetworkUtils::canConnect(url, msg)) + { + msgs.push_back(CStatusMessage(cats, CStatusMessage::SeverityError, msg)); + return msgs; + } + + QUrlQuery params; + params.addQueryItem("username", un); + params.addQueryItem("password", pw); + if (sApp->getGlobalSetup().dbDebugFlag()) { CNetworkUtils::addDebugFlag(params); } + + QString query = params.toString(); + const QNetworkRequest request(CNetworkUtils::getNetworkRequest(url, CNetworkUtils::PostUrlEncoded)); + sApp->postToNetwork(request, query.toUtf8(), { this, &CDatabaseAuthenticationService::ps_parseServerResponse}); + QString rm("Sent request to authentication server %1"); + msgs.push_back(CStatusMessage(cats, CStatusMessage::SeverityInfo, rm.arg(url.toQString()))); + return msgs; + } + + void CDatabaseAuthenticationService::logoff() + { + CUrl url(sApp->getGlobalSetup().getDbLoginServiceUrl()); + url.setQuery("logoff=true"); + QNetworkRequest request(CNetworkUtils::getNetworkRequest(url)); + sApp->getFromNetwork(request, { this, &CDatabaseAuthenticationService::ps_parseServerResponse }); + this->m_swiftDbUser.set(CAuthenticatedUser()); + } + + void CDatabaseAuthenticationService::ps_parseServerResponse(QNetworkReply *nwReplyPtr) + { + // always cleanup reply + QScopedPointer nwReply(nwReplyPtr); + + if (this->m_shutdown) { return; } + QString urlString(nwReply->url().toString()); + if (urlString.toLower().contains("logoff")) + { + sApp->deleteAllCookies(); + emit logoffFinished(); + return; + } + + if (nwReply->error() == QNetworkReply::NoError) + { + QString json(nwReply->readAll()); + if (json.isEmpty()) + { + CLogMessage(this).error("Authentication failed, no response from %1") << urlString; + return; + } + QJsonObject jsonObj(Json::jsonObjectFromString(json)); + CAuthenticatedUser user(CAuthenticatedUser::fromDatabaseJson(jsonObj)); + + CStatusMessageList msgs; + static const CLogCategoryList cats(CLogCategoryList(this).join({ CLogCategory::validation()})); + + if (!user.isAuthenticated() || !user.isValid()) + { + msgs.push_back(CStatusMessage(cats, CStatusMessage::SeverityError, "Cannot login, user or password wrong")); + } + else + { + if (!user.isEnabled()) + { + msgs.push_back(CStatusMessage(cats, CStatusMessage::SeverityError, "User is disabled")); + } + if (user.getRoles().isEmpty()) + { + msgs.push_back(CStatusMessage(cats, CStatusMessage::SeverityError, "User has no roles")); + } + } + this->m_swiftDbUser.set(user); + emit userAuthenticationFinished(user, msgs); + } + else + { + CLogMessage(this).error("Authentication failed, %1") << nwReply->errorString(); + return; + } + } + + void CDatabaseAuthenticationService::ps_userChanged() + { + // code goes here + } + } // ns +} // ns diff --git a/src/blackcore/db/databaseauthentication.h b/src/blackcore/db/databaseauthentication.h new file mode 100644 index 000000000..40e127dbe --- /dev/null +++ b/src/blackcore/db/databaseauthentication.h @@ -0,0 +1,70 @@ +/* Copyright (C) 2015 + * swift project Community / Contributors + * + * This file is part of swift project. It is subject to the license terms in the LICENSE file found in the top-level + * directory of this distribution and at http://www.swift-project.org/license.html. No part of swift project, + * including this file, may be copied, modified, propagated, or distributed except according to the terms + * contained in the LICENSE file. + */ + +//! \file + +#ifndef BLACKCORE_DATABASE_CDATABASEUATHENTICATIONSERVICE_H +#define BLACKCORE_DATABASE_CDATABASEUATHENTICATIONSERVICE_H + +#include "blackcore/blackcoreexport.h" +#include "blackcore/data/authenticateduser.h" +#include "blackmisc/datacache.h" +#include "blackmisc/statusmessagelist.h" + +#include +#include + +class QNetworkReply; +namespace BlackMisc { namespace Network { class CAuthenticatedUser; } } + +namespace BlackCore +{ + namespace Db + { + //! Databse user used with swift DB. Features role and cookie handling. + class BLACKCORE_EXPORT CDatabaseAuthenticationService: public QObject + { + Q_OBJECT + + public: + //! Constructor + CDatabaseAuthenticationService(QObject *parent = nullptr); + + //! Shutdown + void gracefulShutdown(); + + public slots: + //! Try to login to authentication web service + BlackMisc::CStatusMessageList login(const QString &id, const QString &password); + + //! Logoff + void logoff(); + + signals: + //! User authenticated + void userAuthenticationFinished(const BlackMisc::Network::CAuthenticatedUser &user, const BlackMisc::CStatusMessageList &loginStatus); + + //! Logoff completed + void logoffFinished(); + + private slots: + //! Parse login answer + void ps_parseServerResponse(QNetworkReply *nwReplyPtr); + + //! User object changed + void ps_userChanged(); + + private: + BlackMisc::CData m_swiftDbUser {this, &CDatabaseAuthenticationService::ps_userChanged}; + bool m_shutdown = false; + }; + } // ns +} // ns + +#endif // guard diff --git a/src/blackcore/db/databasewriter.cpp b/src/blackcore/db/databasewriter.cpp new file mode 100644 index 000000000..526314c1c --- /dev/null +++ b/src/blackcore/db/databasewriter.cpp @@ -0,0 +1,143 @@ +/* Copyright (C) 2015 + * swift project Community / Contributors + * + * This file is part of swift project. It is subject to the license terms in the LICENSE file found in the top-level + * directory of this distribution and at http://www.swift-project.org/license.html. No part of swift project, + * including this file, may be copied, modified, propagated, or distributed except according to the terms + * contained in the LICENSE file. + */ + +#include "blackcore/application.h" +#include "blackcore/data/globalsetup.h" +#include "blackcore/db/databasewriter.h" +#include "blackmisc/db/datastoreutility.h" +#include "blackmisc/logcategory.h" +#include "blackmisc/logcategorylist.h" +#include "blackmisc/network/networkutils.h" +#include "blackmisc/statusmessage.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace BlackMisc; +using namespace BlackMisc::Network; +using namespace BlackMisc::Simulation; + +namespace BlackCore +{ + namespace Db + { + CDatabaseWriter::CDatabaseWriter(const Network::CUrl &baseUrl, QObject *parent) : + QObject(parent), + m_modelPublishUrl(getModelPublishUrl(baseUrl)) + { + // void + } + + CStatusMessageList CDatabaseWriter::asyncPublishModels(const CAircraftModelList &models) + { + CStatusMessageList msgs; + if (m_shutdown) + { + msgs.push_back(CStatusMessage(CStatusMessage::SeverityWarning, "Database writer shuts down")); + return msgs; + } + + if (m_pendingReply) + { + msgs.push_back(CStatusMessage(CStatusMessage::SeverityWarning, "Another write operation in progress")); + return msgs; + } + + QUrl url(m_modelPublishUrl.toQUrl()); + QNetworkRequest request(url); + CNetworkUtils::ignoreSslVerification(request); + QHttpMultiPart *multiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType, this); + multiPart->append(CNetworkUtils::getJsonTextMultipart(models.toDatabaseJson())); + if (sApp->getGlobalSetup().dbDebugFlag()) + { + multiPart->append(CNetworkUtils::getMultipartWithDebugFlag()); + } + + m_pendingReply = sApp->postToNetwork(request, multiPart, { this, &CDatabaseWriter::ps_postResponse}); + multiPart->setParent(m_pendingReply); + return msgs; + } + + void CDatabaseWriter::gracefulShutdown() + { + m_shutdown = true; + if (m_pendingReply) + { + m_pendingReply->abort(); + m_pendingReply = nullptr; + } + } + + void CDatabaseWriter::ps_postResponse(QNetworkReply *nwReplyPtr) + { + static const CLogCategoryList cats(CLogCategoryList(this).join({ CLogCategory::swiftDbWebservice()})); + QScopedPointer nwReply(nwReplyPtr); + m_pendingReply = nullptr; + QUrl url(nwReply->url()); + QString urlString(url.toString()); + + if (m_shutdown) + { + nwReply->abort(); + return; + } + + if (nwReply->error() == QNetworkReply::NoError) + { + const QString dataFileData(nwReply->readAll().trimmed()); + nwReply->close(); // close asap + if (dataFileData.isEmpty()) + { + const CStatusMessageList msgs({CStatusMessage(cats, CStatusMessage::SeverityError, "No response data from " + urlString)}); + emit published(CAircraftModelList(), CAircraftModelList(), msgs); + return; + } + + CAircraftModelList modelsPublished; + CAircraftModelList modelsSkipped; + CStatusMessageList msgs; + bool success = CDatastoreUtility::parseSwiftPublishResponse(dataFileData, modelsPublished, modelsSkipped, msgs); + emit published(modelsPublished, modelsSkipped, msgs); + Q_UNUSED(success); + } + else + { + QString error = nwReply->errorString(); + nwReply->close(); // close asap + const CStatusMessageList msgs( {CStatusMessage(cats, CStatusMessage::SeverityError, "HTTP error: " + error)}); + emit published(CAircraftModelList(), CAircraftModelList(), msgs); + } + } + + CUrl CDatabaseWriter::getModelPublishUrl(const Network::CUrl &baseUrl) + { + return baseUrl.withAppendedPath("service/publishmodels.php"); + } + + QList CDatabaseWriter::splitData(const QByteArray &data, int size) + { + if (data.size() <= size) { return QList({data}); } + int pos = 0, arrsize = data.size(); + QList arrays; + while (pos < arrsize) + { + QByteArray arr = data.mid(pos, size); + arrays << arr; + pos += arr.size(); + } + return arrays; + } + } // ns +} // ns diff --git a/src/blackcore/db/databasewriter.h b/src/blackcore/db/databasewriter.h new file mode 100644 index 000000000..523c73bb7 --- /dev/null +++ b/src/blackcore/db/databasewriter.h @@ -0,0 +1,67 @@ +/* Copyright (C) 2015 + * swift project Community / Contributors + * + * This file is part of swift project. It is subject to the license terms in the LICENSE file found in the top-level + * directory of this distribution and at http://www.swift-project.org/license.html. No part of swift project, + * including this file, may be copied, modified, propagated, or distributed except according to the terms + * contained in the LICENSE file. + */ + +//! \file + +#ifndef BLACKCORE_DATABASE_WRITER_H +#define BLACKCORE_DATABASE_WRITER_H + +#include "blackcore/blackcoreexport.h" +#include "blackmisc/network/url.h" +#include "blackmisc/simulation/aircraftmodellist.h" +#include "blackmisc/statusmessagelist.h" + +#include +#include +#include + +class QNetworkReply; + +namespace BlackCore +{ + namespace Db + { + //! Write to the swift DB + class BLACKCORE_EXPORT CDatabaseWriter : public QObject + { + Q_OBJECT + + public: + //! Constructor + CDatabaseWriter(const BlackMisc::Network::CUrl &baseUrl, QObject *parent); + + //! Write model to DB + BlackMisc::CStatusMessageList asyncPublishModels(const BlackMisc::Simulation::CAircraftModelList &models); + + //! Shutdown + void gracefulShutdown(); + + signals: + //! Published models, the response to \sa asyncPublishModels + void published(const BlackMisc::Simulation::CAircraftModelList &modelsPublished, const BlackMisc::Simulation::CAircraftModelList &modelsSkipped, const BlackMisc::CStatusMessageList &messages); + + private slots: + //! Post response + void ps_postResponse(QNetworkReply *nwReplyPtr); + + private: + BlackMisc::Network::CUrl m_modelPublishUrl; + QNetworkReply *m_pendingReply = nullptr; + bool m_shutdown = false; + + //! URL model web service + static BlackMisc::Network::CUrl getModelPublishUrl(const BlackMisc::Network::CUrl &baseUrl); + + //! Split data array + static QList splitData(const QByteArray &data, int size); + }; + } // ns +} // ns + +#endif // guard diff --git a/src/blackcore/db/modeldatareader.cpp b/src/blackcore/db/modeldatareader.cpp new file mode 100644 index 000000000..d5363c67b --- /dev/null +++ b/src/blackcore/db/modeldatareader.cpp @@ -0,0 +1,473 @@ +/* Copyright (C) 2015 + * swift project Community / Contributors + * + * This file is part of swift project. It is subject to the license terms in the LICENSE file found in the top-level + * directory of this distribution and at http://www.swift-project.org/license.html. No part of swift project, + * including this file, may be copied, modified, propagated, or distributed except according to the terms + * contained in the LICENSE file. + */ + +#include "blackcore/application.h" +#include "blackcore/data/globalsetup.h" +#include "blackcore/db/modeldatareader.h" +#include "blackmisc/fileutils.h" +#include "blackmisc/json.h" +#include "blackmisc/logmessage.h" +#include "blackmisc/statusmessage.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace BlackMisc; +using namespace BlackMisc::Aviation; +using namespace BlackMisc::Simulation; +using namespace BlackMisc::Network; +using namespace BlackCore::Data; + +namespace BlackCore +{ + namespace Db + { + CModelDataReader::CModelDataReader(QObject *owner, const CDatabaseReaderConfigList &config) : + CDatabaseReader(owner, config, "CModelDataReader") + { + // void + } + + CLiveryList CModelDataReader::getLiveries() const + { + QReadLocker l(&m_lockLivery); + return m_liveries; + } + + CLivery CModelDataReader::getLiveryForCombinedCode(const QString &combinedCode) const + { + if (!CLivery::isValidCombinedCode(combinedCode)) { return CLivery(); } + CLiveryList liveries(getLiveries()); + return liveries.findByCombinedCode(combinedCode); + } + + CLivery CModelDataReader::getStdLiveryForAirlineCode(const CAirlineIcaoCode &icao) const + { + if (!icao.hasValidDesignator()) { return CLivery(); } + CLiveryList liveries(getLiveries()); + return liveries.findStdLiveryByAirlineIcaoDesignator(icao); + } + + CLivery CModelDataReader::getLiveryForDbKey(int id) const + { + if (id < 0) { return CLivery(); } + CLiveryList liveries(getLiveries()); + return liveries.findByKey(id); + } + + CLivery CModelDataReader::smartLiverySelector(const CLivery &liveryPattern) const + { + CLiveryList liveries(getLiveries()); // thread safe copy + return liveries.smartLiverySelector(liveryPattern); + } + + CDistributorList CModelDataReader::getDistributors() const + { + QReadLocker l(&m_lockDistributor); + return m_distributors; + } + + CAircraftModelList CModelDataReader::getModels() const + { + QReadLocker l(&m_lockModels); + return m_models; + } + + CAircraftModel CModelDataReader::getModelForModelString(const QString &modelString) const + { + if (modelString.isEmpty()) { return CAircraftModel(); } + CAircraftModelList models(getModels()); + return models.findFirstByModelStringOrDefault(modelString); + } + + CAircraftModelList CModelDataReader::getModelsForAircraftDesignatorAndLiveryCombinedCode(const QString &aircraftDesignator, const QString &combinedCode) + { + if (aircraftDesignator.isEmpty()) { return CAircraftModelList(); } + CAircraftModelList models(getModels()); + return models.findByAircraftDesignatorAndLiveryCombinedCode(aircraftDesignator, combinedCode); + } + + int CModelDataReader::getLiveriesCount() const + { + QReadLocker l(&m_lockLivery); + return m_liveries.size(); + } + + int CModelDataReader::getDistributorsCount() const + { + QReadLocker l(&m_lockDistributor); + return m_distributors.size(); + } + + CDistributor CModelDataReader::smartDistributorSelector(const CDistributor &distributorPattern) const + { + CDistributorList distributors(getDistributors()); // thread safe copy + return distributors.smartDistributorSelector(distributorPattern); + } + + int CModelDataReader::getModelsCount() const + { + QReadLocker l(&m_lockModels); + return m_models.size(); + } + + QList CModelDataReader::getModelDbKeys() const + { + QReadLocker l(&m_lockModels); + return m_models.toDbKeyList(); + } + + QStringList CModelDataReader::getModelStrings() const + { + QReadLocker l(&m_lockModels); + return m_models.getModelStrings(false); + } + + bool CModelDataReader::areAllDataRead() const + { + return + getLiveriesCount() > 0 && + getModelsCount() > 0 && + getDistributorsCount() > 0; + } + + void CModelDataReader::ps_read(CEntityFlags::Entity entity, const QDateTime &newerThan) + { + this->threadAssertCheck(); + + CEntityFlags::Entity triggeredRead = CEntityFlags::NoEntity; + if (entity.testFlag(CEntityFlags::LiveryEntity)) + { + CUrl url(getLiveryUrl()); + if (!url.isEmpty()) + { + if (!newerThan.isNull()) + { + const QString tss(newerThan.toString(Qt::ISODate)); + url.appendQuery(QString(parameterLatestTimestamp() + "=" + tss)); + } + sApp->getFromNetwork(url, { this, &CModelDataReader::ps_parseLiveryData}); + triggeredRead |= CEntityFlags::LiveryEntity; + } + else + { + CLogMessage(this).error("No URL for %1") << CEntityFlags::flagToString(CEntityFlags::LiveryEntity); + } + } + + if (entity.testFlag(CEntityFlags::DistributorEntity)) + { + CUrl url(getDistributorUrl()); + if (!url.isEmpty()) + { + if (!newerThan.isNull()) + { + const QString tss(newerThan.toString(Qt::ISODate)); + url.appendQuery(QString(parameterLatestTimestamp() + "=" + tss)); + } + sApp->getFromNetwork(url, { this, &CModelDataReader::ps_parseDistributorData}); + triggeredRead |= CEntityFlags::DistributorEntity; + } + else + { + CLogMessage(this).error("No URL for %1") << CEntityFlags::flagToString(CEntityFlags::DistributorEntity); + } + } + + if (entity.testFlag(CEntityFlags::ModelEntity)) + { + CUrl url(getModelUrl()); + if (!url.isEmpty()) + { + if (!newerThan.isNull()) + { + const QString tss(newerThan.toString(Qt::ISODate)); + url.appendQuery(QString(parameterLatestTimestamp() + "=" + tss)); + } + sApp->getFromNetwork(url, { this, &CModelDataReader::ps_parseModelData}); + triggeredRead |= CEntityFlags::ModelEntity; + } + else + { + CLogMessage(this).error("No URL for %1") << CEntityFlags::flagToString(CEntityFlags::ModelEntity); + } + } + + if (triggeredRead != CEntityFlags::NoEntity) + { + emit dataRead(triggeredRead, CEntityFlags::StartRead, 0); + } + } + + void CModelDataReader::ps_parseLiveryData(QNetworkReply *nwReplyPtr) + { + // wrap pointer, make sure any exit cleans up reply + // required to use delete later as object is created in a different thread + QScopedPointer nwReply(nwReplyPtr); + QString urlString(nwReply->url().toString()); + CDatabaseReader::JsonDatastoreResponse res = this->setStatusAndTransformReplyIntoDatastoreResponse(nwReply.data()); + if (res.hasErrorMessage()) + { + CLogMessage::preformatted(res.lastWarningOrAbove()); + emit dataRead(CEntityFlags::LiveryEntity, CEntityFlags::ReadFailed, 0); + return; + } + + // get all or incremental set of distributor + CLiveryList liveries; + if (res.isRestricted()) + { + // create full list if it was just incremental + liveries = this->getLiveries(); + liveries.replaceOrAddObjectsByKey(CLiveryList::fromDatabaseJson(res)); + } + else + { + liveries = CLiveryList::fromDatabaseJson(res); + } + + // this part needs to be synchronized + int n = liveries.size(); + { + QWriteLocker wl(&this->m_lockLivery); + this->m_liveries = liveries; + } + + // never emit when lock is held -> deadlock + emit dataRead(CEntityFlags::LiveryEntity, + res.isRestricted() ? CEntityFlags::ReadFinishedRestricted : CEntityFlags::ReadFinished, n); + CLogMessage(this).info("Read %1 %2 from %3") << n << CEntityFlags::flagToString(CEntityFlags::LiveryEntity) << urlString; + } + + void CModelDataReader::ps_parseDistributorData(QNetworkReply *nwReplyPtr) + { + QScopedPointer nwReply(nwReplyPtr); + QString urlString(nwReply->url().toString()); + CDatabaseReader::JsonDatastoreResponse res = this->setStatusAndTransformReplyIntoDatastoreResponse(nwReply.data()); + if (res.hasErrorMessage()) + { + CLogMessage::preformatted(res.lastWarningOrAbove()); + emit dataRead(CEntityFlags::DistributorEntity, CEntityFlags::ReadFailed, 0); + return; + } + + // get all or incremental set of distributor + CDistributorList distributors; + if (res.isRestricted()) + { + // create full list if it was just incremental + distributors = this->getDistributors(); + distributors.replaceOrAddObjectsByKey(CDistributorList::fromDatabaseJson(res)); + } + else + { + distributors = CDistributorList::fromDatabaseJson(res); + } + + // this part needs to be synchronized + int n = distributors.size(); + { + QWriteLocker wl(&this->m_lockDistributor); + this->m_distributors = distributors; + } + emit dataRead(CEntityFlags::DistributorEntity, + res.isRestricted() ? CEntityFlags::ReadFinishedRestricted : CEntityFlags::ReadFinished, n); + CLogMessage(this).info("Read %1 %2 from %3") << n << CEntityFlags::flagToString(CEntityFlags::DistributorEntity) << urlString; + } + + void CModelDataReader::ps_parseModelData(QNetworkReply *nwReplyPtr) + { + QScopedPointer nwReply(nwReplyPtr); + QString urlString(nwReply->url().toString()); + CDatabaseReader::JsonDatastoreResponse res = this->setStatusAndTransformReplyIntoDatastoreResponse(nwReply.data()); + if (res.hasErrorMessage()) + { + CLogMessage::preformatted(res.lastWarningOrAbove()); + emit dataRead(CEntityFlags::ModelEntity, CEntityFlags::ReadFailed, 0); + return; + } + + // get all or incremental set of models + CAircraftModelList models; + if (res.isRestricted()) + { + // create full list if it was just incremental + models = this->getModels(); + models.replaceOrAddObjectsByKey(CAircraftModelList::fromDatabaseJson(res)); + } + else + { + models = CAircraftModelList::fromDatabaseJson(res); + } + + // syncronized update + int n = models.size(); + { + QWriteLocker wl(&this->m_lockModels); + this->m_models = models; + } + + emit dataRead(CEntityFlags::ModelEntity, + res.isRestricted() ? CEntityFlags::ReadFinishedRestricted : CEntityFlags::ReadFinished, n); + CLogMessage(this).info("Read %1 %2 from %3") << n << CEntityFlags::flagToString(CEntityFlags::ModelEntity) << urlString; + } + + bool CModelDataReader::readFromJsonFiles(const QString &dir, CEntityFlags::Entity whatToRead) + { + QDir directory(dir); + if (!directory.exists()) { return false; } + BlackMisc::Network::CEntityFlags::Entity reallyRead = CEntityFlags::NoEntity; + + if (whatToRead.testFlag(CEntityFlags::LiveryEntity)) + { + QString liveriesJson(CFileUtils::readFileToString(CFileUtils::appendFilePaths(directory.absolutePath(), "liveries.json"))); + if (!liveriesJson.isEmpty()) + { + CLiveryList liveries; + liveries.convertFromJson(liveriesJson); + int c = liveries.size(); + { + QWriteLocker l(&m_lockLivery); + m_liveries = liveries; + } + // never emit when lcok is held -> deadlock + emit dataRead(CEntityFlags::LiveryEntity, CEntityFlags::ReadFinished, c); + reallyRead |= CEntityFlags::LiveryEntity; + } + } + + if (whatToRead.testFlag(CEntityFlags::ModelEntity)) + { + QString modelsJson(CFileUtils::readFileToString(CFileUtils::appendFilePaths(directory.absolutePath(), "models.json"))); + if (!modelsJson.isEmpty()) + { + CAircraftModelList models; + models.convertFromJson(Json::jsonObjectFromString(modelsJson)); + int c = models.size(); + { + QWriteLocker l(&m_lockModels); + m_models = models; + } + emit dataRead(CEntityFlags::ModelEntity, CEntityFlags::ReadFinished, c); + reallyRead |= CEntityFlags::ModelEntity; + } + } + + if (whatToRead.testFlag(CEntityFlags::DistributorEntity)) + { + QString distributorsJson(CFileUtils::readFileToString(CFileUtils::appendFilePaths(directory.absolutePath(), "distributors.json"))); + if (!distributorsJson.isEmpty()) + { + CDistributorList distributors; + distributors.convertFromJson(Json::jsonObjectFromString(distributorsJson)); + int c = distributors.size(); + { + QWriteLocker l(&m_lockDistributor); + m_distributors = distributors; + } + reallyRead |= CEntityFlags::DistributorEntity; + emit dataRead(CEntityFlags::DistributorEntity, CEntityFlags::ReadFinished, c); + } + } + + return (reallyRead & CEntityFlags::DistributorLiveryModel) == whatToRead; + } + + bool CModelDataReader::readFromJsonFilesInBackground(const QString &dir, CEntityFlags::Entity whatToRead) + { + if (dir.isEmpty() || whatToRead == CEntityFlags::NoEntity) { return false; } + QTimer::singleShot(0, this, [this, dir, whatToRead]() + { + bool s = this->readFromJsonFiles(dir, whatToRead); + Q_UNUSED(s); + }); + return true; + } + + bool CModelDataReader::writeToJsonFiles(const QString &dir) const + { + QDir directory(dir); + if (!directory.exists()) { return false; } + if (this->getLiveriesCount() > 0) + { + QString json(QJsonDocument(this->getLiveries().toJson()).toJson()); + bool s = CFileUtils::writeStringToFileInBackground(json, CFileUtils::appendFilePaths(directory.absolutePath(), "liveries.json")); + if (!s) { return false; } + } + + if (this->getModelsCount() > 0) + { + QString json(QJsonDocument(this->getModels().toJson()).toJson()); + bool s = CFileUtils::writeStringToFileInBackground(json, CFileUtils::appendFilePaths(directory.absolutePath(), "models.json")); + if (!s) { return false; } + } + + if (this->getDistributorsCount() > 0) + { + QString json(QJsonDocument(this->getDistributors().toJson()).toJson()); + bool s = CFileUtils::writeStringToFileInBackground(json, CFileUtils::appendFilePaths(directory.absolutePath(), "distributors.json")); + if (!s) { return false; } + } + return true; + } + + void CModelDataReader::syncronizeCaches(CEntityFlags::Entity entities) + { + Q_UNUSED(entities); + } + + void CModelDataReader::invalidateCaches(CEntityFlags::Entity entities) + { + Q_UNUSED(entities); + } + + QDateTime CModelDataReader::getCacheTimestamp(CEntityFlags::Entity entity) + { + Q_UNUSED(entity); + return QDateTime(); + } + + CUrl CModelDataReader::getBaseUrl() const + { + const CUrl baseUrl(sApp->getGlobalSetup().getDbModelReaderUrl()); + return baseUrl; + } + + CUrl CModelDataReader::getLiveryUrl(bool shared) const + { + return shared ? + getBaseUrl().withAppendedPath("service/jsonlivery.php") : + getBaseUrl().withAppendedPath("service/jsonlivery.php"); + } + + CUrl CModelDataReader::getDistributorUrl(bool shared) const + { + return shared ? + getBaseUrl().withAppendedPath("service/jsondistributor.php") : + getBaseUrl().withAppendedPath("service/jsondistributor.php"); + } + + CUrl CModelDataReader::getModelUrl(bool shared) const + { + return shared ? + getBaseUrl().withAppendedPath("service/jsonaircraftmodel.php") : + getBaseUrl().withAppendedPath("service/jsonaircraftmodel.php"); + } + } // ns +} // ns diff --git a/src/blackcore/db/modeldatareader.h b/src/blackcore/db/modeldatareader.h new file mode 100644 index 000000000..8793d39e9 --- /dev/null +++ b/src/blackcore/db/modeldatareader.h @@ -0,0 +1,174 @@ +/* Copyright (C) 2015 + * swift project Community / Contributors + * + * This file is part of swift project. It is subject to the license terms in the LICENSE file found in the top-level + * directory of this distribution and at http://www.swift-project.org/license.html. No part of swift project, + * including this file, may be copied, modified, propagated, or distributed except according to the terms + * contained in the LICENSE file. + */ + +//! \file + +#ifndef BLACKCORE_MODELDATAREADER_H +#define BLACKCORE_MODELDATAREADER_H + +#include "blackcore/blackcoreexport.h" +#include "blackcore/db/databasereader.h" +#include "blackmisc/aviation/airlineicaocode.h" +#include "blackmisc/aviation/livery.h" +#include "blackmisc/aviation/liverylist.h" +#include "blackmisc/network/entityflags.h" +#include "blackmisc/network/url.h" +#include "blackmisc/simulation/aircraftmodel.h" +#include "blackmisc/simulation/aircraftmodellist.h" +#include "blackmisc/simulation/distributor.h" +#include "blackmisc/simulation/distributorlist.h" + +#include +#include +#include +#include +#include +#include + +class QNetworkReply; + +namespace BlackCore +{ + namespace Db + { + //! Read model related data from Database + class BLACKCORE_EXPORT CModelDataReader : public CDatabaseReader + { + Q_OBJECT + + public: + //! Constructor + explicit CModelDataReader(QObject *owner, const CDatabaseReaderConfigList &config); + + //! Get aircraft liveries + //! \threadsafe + BlackMisc::Aviation::CLiveryList getLiveries() const; + + //! Get aircraft livery for code + //! \threadsafe + BlackMisc::Aviation::CLivery getLiveryForCombinedCode(const QString &combinedCode) const; + + //! Get aircraft livery for ICAO code + //! \threadsafe + BlackMisc::Aviation::CLivery getStdLiveryForAirlineCode(const BlackMisc::Aviation::CAirlineIcaoCode &icao) const; + + //! Get aircraft livery for id + //! \threadsafe + BlackMisc::Aviation::CLivery getLiveryForDbKey(int id) const; + + //! Best match specified by livery + //! \threadsafe + BlackMisc::Aviation::CLivery smartLiverySelector(const BlackMisc::Aviation::CLivery &livery) const; + + //! Get distributors (of models) + //! \threadsafe + BlackMisc::Simulation::CDistributorList getDistributors() const; + + //! Get models + //! \threadsafe + BlackMisc::Simulation::CAircraftModelList getModels() const; + + //! Get model for string + //! \threadsafe + BlackMisc::Simulation::CAircraftModel getModelForModelString(const QString &modelString) const; + + //! Get model for designator/combined code + //! \threadsafe + BlackMisc::Simulation::CAircraftModelList getModelsForAircraftDesignatorAndLiveryCombinedCode(const QString &aircraftDesignator, const QString &combinedCode); + + //! Get aircraft liveries count + //! \threadsafe + int getLiveriesCount() const; + + //! Get model distributors count + //! \threadsafe + int getDistributorsCount() const; + + //! Best match specified by distributor + //! \threadsafe + BlackMisc::Simulation::CDistributor smartDistributorSelector(const BlackMisc::Simulation::CDistributor &distributorPattern) const; + + //! Get models count + //! \threadsafe + int getModelsCount() const; + + //! Get model keys + //! \threadsafe + QList getModelDbKeys() const; + + //! Get model keys + //! \threadsafe + QStringList getModelStrings() const; + + //! All data read? + //! \threadsafe + bool areAllDataRead() const; + + //! Read to JSON file + bool readFromJsonFiles(const QString &dir, BlackMisc::Network::CEntityFlags::Entity whatToRead = BlackMisc::Network::CEntityFlags::DistributorLiveryModel); + + //! Read from static DB data file + bool readFromJsonFilesInBackground(const QString &dir, BlackMisc::Network::CEntityFlags::Entity whatToRead = BlackMisc::Network::CEntityFlags::DistributorLiveryModel); + + //! Write to JSON file + bool writeToJsonFiles(const QString &dir) const; + + signals: + //! Combined read signal + void dataRead(BlackMisc::Network::CEntityFlags::Entity entity, BlackMisc::Network::CEntityFlags::ReadState state, int number); + + protected: + //! \name cache handling for base class + //! @{ + virtual void syncronizeCaches(BlackMisc::Network::CEntityFlags::Entity entities) override; + virtual void invalidateCaches(BlackMisc::Network::CEntityFlags::Entity entities) override; + virtual QDateTime getCacheTimestamp(BlackMisc::Network::CEntityFlags::Entity entity) override; + //! @} + + private slots: + //! Liveries have been read + void ps_parseLiveryData(QNetworkReply *nwReply); + + //! Distributors have been read + void ps_parseDistributorData(QNetworkReply *nwReply); + + //! Models have been read + void ps_parseModelData(QNetworkReply *nwReply); + + //! Read / re-read data file + void ps_read(BlackMisc::Network::CEntityFlags::Entity entity = BlackMisc::Network::CEntityFlags::DistributorLiveryModel, const QDateTime &newerThan = QDateTime()); + + private: + BlackMisc::Aviation::CLiveryList m_liveries; + BlackMisc::Simulation::CDistributorList m_distributors; + BlackMisc::Simulation::CAircraftModelList m_models; + BlackMisc::Network::CUrl m_urlLiveries; + BlackMisc::Network::CUrl m_urlDistributors; + BlackMisc::Network::CUrl m_urlModels; + + mutable QReadWriteLock m_lockDistributor; + mutable QReadWriteLock m_lockLivery; + mutable QReadWriteLock m_lockModels; + + //! Base URL + BlackMisc::Network::CUrl getBaseUrl() const; + + //! URL livery web service + BlackMisc::Network::CUrl getLiveryUrl(bool shared = false) const; + + //! URL distributor web service + BlackMisc::Network::CUrl getDistributorUrl(bool shared = false) const; + + //! URL model web service + BlackMisc::Network::CUrl getModelUrl(bool shared = false) const; + }; + } // ns +} // ns + +#endif // guard diff --git a/src/blackcore/modeldatareader.cpp b/src/blackcore/modeldatareader.cpp deleted file mode 100644 index 16d9ac337..000000000 --- a/src/blackcore/modeldatareader.cpp +++ /dev/null @@ -1,461 +0,0 @@ -/* Copyright (C) 2015 - * swift project Community / Contributors - * - * This file is part of swift project. It is subject to the license terms in the LICENSE file found in the top-level - * directory of this distribution and at http://www.swift-project.org/license.html. No part of swift project, - * including this file, may be copied, modified, propagated, or distributed except according to the terms - * contained in the LICENSE file. - */ - -#include "blackcore/application.h" -#include "blackcore/data/globalsetup.h" -#include "blackcore/modeldatareader.h" -#include "blackmisc/fileutils.h" -#include "blackmisc/json.h" -#include "blackmisc/logmessage.h" -#include "blackmisc/statusmessage.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -using namespace BlackMisc; -using namespace BlackMisc::Aviation; -using namespace BlackMisc::Simulation; -using namespace BlackMisc::Network; -using namespace BlackCore::Data; - -namespace BlackCore -{ - CModelDataReader::CModelDataReader(QObject *owner) : - CDatabaseReader(owner, "CModelDataReader") - { - // void - } - - CLiveryList CModelDataReader::getLiveries() const - { - QReadLocker l(&m_lockLivery); - return m_liveries; - } - - CLivery CModelDataReader::getLiveryForCombinedCode(const QString &combinedCode) const - { - if (!CLivery::isValidCombinedCode(combinedCode)) { return CLivery(); } - CLiveryList liveries(getLiveries()); - return liveries.findByCombinedCode(combinedCode); - } - - CLivery CModelDataReader::getStdLiveryForAirlineCode(const CAirlineIcaoCode &icao) const - { - if (!icao.hasValidDesignator()) { return CLivery(); } - CLiveryList liveries(getLiveries()); - return liveries.findStdLiveryByAirlineIcaoDesignator(icao); - } - - CLivery CModelDataReader::getLiveryForDbKey(int id) const - { - if (id < 0) { return CLivery(); } - CLiveryList liveries(getLiveries()); - return liveries.findByKey(id); - } - - CLivery CModelDataReader::smartLiverySelector(const CLivery &liveryPattern) const - { - CLiveryList liveries(getLiveries()); // thread safe copy - return liveries.smartLiverySelector(liveryPattern); - } - - CDistributorList CModelDataReader::getDistributors() const - { - QReadLocker l(&m_lockDistributor); - return m_distributors; - } - - CAircraftModelList CModelDataReader::getModels() const - { - QReadLocker l(&m_lockModels); - return m_models; - } - - CAircraftModel CModelDataReader::getModelForModelString(const QString &modelString) const - { - if (modelString.isEmpty()) { return CAircraftModel(); } - CAircraftModelList models(getModels()); - return models.findFirstByModelStringOrDefault(modelString); - } - - CAircraftModelList CModelDataReader::getModelsForAircraftDesignatorAndLiveryCombinedCode(const QString &aircraftDesignator, const QString &combinedCode) - { - if (aircraftDesignator.isEmpty()) { return CAircraftModelList(); } - CAircraftModelList models(getModels()); - return models.findByAircraftDesignatorAndLiveryCombinedCode(aircraftDesignator, combinedCode); - } - - int CModelDataReader::getLiveriesCount() const - { - QReadLocker l(&m_lockLivery); - return m_liveries.size(); - } - - int CModelDataReader::getDistributorsCount() const - { - QReadLocker l(&m_lockDistributor); - return m_distributors.size(); - } - - CDistributor CModelDataReader::smartDistributorSelector(const CDistributor &distributorPattern) const - { - CDistributorList distributors(getDistributors()); // thread safe copy - return distributors.smartDistributorSelector(distributorPattern); - } - - int CModelDataReader::getModelsCount() const - { - QReadLocker l(&m_lockModels); - return m_models.size(); - } - - QList CModelDataReader::getModelDbKeys() const - { - QReadLocker l(&m_lockModels); - return m_models.toDbKeyList(); - } - - QStringList CModelDataReader::getModelStrings() const - { - QReadLocker l(&m_lockModels); - return m_models.getModelStrings(false); - } - - bool CModelDataReader::areAllDataRead() const - { - return - getLiveriesCount() > 0 && - getModelsCount() > 0 && - getDistributorsCount() > 0; - } - - void CModelDataReader::ps_read(CEntityFlags::Entity entity, const QDateTime &newerThan) - { - this->threadAssertCheck(); - - CEntityFlags::Entity triggeredRead = CEntityFlags::NoEntity; - if (entity.testFlag(CEntityFlags::LiveryEntity)) - { - CUrl url(getLiveryUrl()); - if (!url.isEmpty()) - { - if (!newerThan.isNull()) - { - const QString tss(newerThan.toString(Qt::ISODate)); - url.appendQuery(QString(parameterLatestTimestamp() + "=" + tss)); - } - sApp->getFromNetwork(url, { this, &CModelDataReader::ps_parseLiveryData}); - triggeredRead |= CEntityFlags::LiveryEntity; - } - else - { - CLogMessage(this).error("No URL for %1") << CEntityFlags::flagToString(CEntityFlags::LiveryEntity); - } - } - - if (entity.testFlag(CEntityFlags::DistributorEntity)) - { - CUrl url(getDistributorUrl()); - if (!url.isEmpty()) - { - if (!newerThan.isNull()) - { - const QString tss(newerThan.toString(Qt::ISODate)); - url.appendQuery(QString(parameterLatestTimestamp() + "=" + tss)); - } - sApp->getFromNetwork(url, { this, &CModelDataReader::ps_parseDistributorData}); - triggeredRead |= CEntityFlags::DistributorEntity; - } - else - { - CLogMessage(this).error("No URL for %1") << CEntityFlags::flagToString(CEntityFlags::DistributorEntity); - } - } - - if (entity.testFlag(CEntityFlags::ModelEntity)) - { - CUrl url(getModelUrl()); - if (!url.isEmpty()) - { - if (!newerThan.isNull()) - { - const QString tss(newerThan.toString(Qt::ISODate)); - url.appendQuery(QString(parameterLatestTimestamp() + "=" + tss)); - } - sApp->getFromNetwork(url, { this, &CModelDataReader::ps_parseModelData}); - triggeredRead |= CEntityFlags::ModelEntity; - } - else - { - CLogMessage(this).error("No URL for %1") << CEntityFlags::flagToString(CEntityFlags::ModelEntity); - } - } - - if (triggeredRead != CEntityFlags::NoEntity) - { - emit dataRead(triggeredRead, CEntityFlags::StartRead, 0); - } - } - - void CModelDataReader::ps_parseLiveryData(QNetworkReply *nwReplyPtr) - { - // wrap pointer, make sure any exit cleans up reply - // required to use delete later as object is created in a different thread - QScopedPointer nwReply(nwReplyPtr); - QString urlString(nwReply->url().toString()); - CDatabaseReader::JsonDatastoreResponse res = this->setStatusAndTransformReplyIntoDatastoreResponse(nwReply.data()); - if (res.hasErrorMessage()) - { - CLogMessage::preformatted(res.lastWarningOrAbove()); - emit dataRead(CEntityFlags::LiveryEntity, CEntityFlags::ReadFailed, 0); - return; - } - - // get all or incremental set of distributor - CLiveryList liveries; - if (res.isRestricted()) - { - // create full list if it was just incremental - liveries = this->getLiveries(); - liveries.replaceOrAddObjectsByKey(CLiveryList::fromDatabaseJson(res)); - } - else - { - liveries = CLiveryList::fromDatabaseJson(res); - } - - // this part needs to be synchronized - int n = liveries.size(); - { - QWriteLocker wl(&this->m_lockLivery); - this->m_liveries = liveries; - } - - // never emit when lock is held -> deadlock - emit dataRead(CEntityFlags::LiveryEntity, - res.isRestricted() ? CEntityFlags::ReadFinishedRestricted : CEntityFlags::ReadFinished, n); - CLogMessage(this).info("Read %1 %2 from %3") << n << CEntityFlags::flagToString(CEntityFlags::LiveryEntity) << urlString; - } - - void CModelDataReader::ps_parseDistributorData(QNetworkReply *nwReplyPtr) - { - QScopedPointer nwReply(nwReplyPtr); - QString urlString(nwReply->url().toString()); - CDatabaseReader::JsonDatastoreResponse res = this->setStatusAndTransformReplyIntoDatastoreResponse(nwReply.data()); - if (res.hasErrorMessage()) - { - CLogMessage::preformatted(res.lastWarningOrAbove()); - emit dataRead(CEntityFlags::DistributorEntity, CEntityFlags::ReadFailed, 0); - return; - } - - // get all or incremental set of distributor - CDistributorList distributors; - if (res.isRestricted()) - { - // create full list if it was just incremental - distributors = this->getDistributors(); - distributors.replaceOrAddObjectsByKey(CDistributorList::fromDatabaseJson(res)); - } - else - { - distributors = CDistributorList::fromDatabaseJson(res); - } - - // this part needs to be synchronized - int n = distributors.size(); - { - QWriteLocker wl(&this->m_lockDistributor); - this->m_distributors = distributors; - } - emit dataRead(CEntityFlags::DistributorEntity, - res.isRestricted() ? CEntityFlags::ReadFinishedRestricted : CEntityFlags::ReadFinished, n); - CLogMessage(this).info("Read %1 %2 from %3") << n << CEntityFlags::flagToString(CEntityFlags::DistributorEntity) << urlString; - } - - void CModelDataReader::ps_parseModelData(QNetworkReply *nwReplyPtr) - { - QScopedPointer nwReply(nwReplyPtr); - QString urlString(nwReply->url().toString()); - CDatabaseReader::JsonDatastoreResponse res = this->setStatusAndTransformReplyIntoDatastoreResponse(nwReply.data()); - if (res.hasErrorMessage()) - { - CLogMessage::preformatted(res.lastWarningOrAbove()); - emit dataRead(CEntityFlags::ModelEntity, CEntityFlags::ReadFailed, 0); - return; - } - - // get all or incremental set of models - CAircraftModelList models; - if (res.isRestricted()) - { - // create full list if it was just incremental - models = this->getModels(); - models.replaceOrAddObjectsByKey(CAircraftModelList::fromDatabaseJson(res)); - } - else - { - models = CAircraftModelList::fromDatabaseJson(res); - } - - // syncronized update - int n = models.size(); - { - QWriteLocker wl(&this->m_lockModels); - this->m_models = models; - } - - emit dataRead(CEntityFlags::ModelEntity, - res.isRestricted() ? CEntityFlags::ReadFinishedRestricted : CEntityFlags::ReadFinished, n); - CLogMessage(this).info("Read %1 %2 from %3") << n << CEntityFlags::flagToString(CEntityFlags::ModelEntity) << urlString; - } - - bool CModelDataReader::readFromJsonFiles(const QString &dir, CEntityFlags::Entity whatToRead) - { - QDir directory(dir); - if (!directory.exists()) { return false; } - BlackMisc::Network::CEntityFlags::Entity reallyRead = CEntityFlags::NoEntity; - - if (whatToRead.testFlag(CEntityFlags::LiveryEntity)) - { - QString liveriesJson(CFileUtils::readFileToString(CFileUtils::appendFilePaths(directory.absolutePath(), "liveries.json"))); - if (!liveriesJson.isEmpty()) - { - CLiveryList liveries; - liveries.convertFromJson(liveriesJson); - int c = liveries.size(); - { - QWriteLocker l(&m_lockLivery); - m_liveries = liveries; - } - // never emit when lcok is held -> deadlock - emit dataRead(CEntityFlags::LiveryEntity, CEntityFlags::ReadFinished, c); - reallyRead |= CEntityFlags::LiveryEntity; - } - } - - if (whatToRead.testFlag(CEntityFlags::ModelEntity)) - { - QString modelsJson(CFileUtils::readFileToString(CFileUtils::appendFilePaths(directory.absolutePath(), "models.json"))); - if (!modelsJson.isEmpty()) - { - CAircraftModelList models; - models.convertFromJson(Json::jsonObjectFromString(modelsJson)); - int c = models.size(); - { - QWriteLocker l(&m_lockModels); - m_models = models; - } - emit dataRead(CEntityFlags::ModelEntity, CEntityFlags::ReadFinished, c); - reallyRead |= CEntityFlags::ModelEntity; - } - } - - if (whatToRead.testFlag(CEntityFlags::DistributorEntity)) - { - QString distributorsJson(CFileUtils::readFileToString(CFileUtils::appendFilePaths(directory.absolutePath(), "distributors.json"))); - if (!distributorsJson.isEmpty()) - { - CDistributorList distributors; - distributors.convertFromJson(Json::jsonObjectFromString(distributorsJson)); - int c = distributors.size(); - { - QWriteLocker l(&m_lockDistributor); - m_distributors = distributors; - } - reallyRead |= CEntityFlags::DistributorEntity; - emit dataRead(CEntityFlags::DistributorEntity, CEntityFlags::ReadFinished, c); - } - } - - return (reallyRead & CEntityFlags::DistributorLiveryModel) == whatToRead; - } - - bool CModelDataReader::readFromJsonFilesInBackground(const QString &dir, CEntityFlags::Entity whatToRead) - { - if (dir.isEmpty() || whatToRead == CEntityFlags::NoEntity) { return false; } - QTimer::singleShot(0, this, [this, dir, whatToRead]() - { - bool s = this->readFromJsonFiles(dir, whatToRead); - Q_UNUSED(s); - }); - return true; - } - - bool CModelDataReader::writeToJsonFiles(const QString &dir) const - { - QDir directory(dir); - if (!directory.exists()) { return false; } - if (this->getLiveriesCount() > 0) - { - QString json(QJsonDocument(this->getLiveries().toJson()).toJson()); - bool s = CFileUtils::writeStringToFileInBackground(json, CFileUtils::appendFilePaths(directory.absolutePath(), "liveries.json")); - if (!s) { return false; } - } - - if (this->getModelsCount() > 0) - { - QString json(QJsonDocument(this->getModels().toJson()).toJson()); - bool s = CFileUtils::writeStringToFileInBackground(json, CFileUtils::appendFilePaths(directory.absolutePath(), "models.json")); - if (!s) { return false; } - } - - if (this->getDistributorsCount() > 0) - { - QString json(QJsonDocument(this->getDistributors().toJson()).toJson()); - bool s = CFileUtils::writeStringToFileInBackground(json, CFileUtils::appendFilePaths(directory.absolutePath(), "distributors.json")); - if (!s) { return false; } - } - return true; - } - - CUrl CModelDataReader::getBaseUrl() const - { - const CUrl baseUrl(sApp->getGlobalSetup().getDbModelReaderUrl()); - return baseUrl; - } - - CUrl CModelDataReader::getLiveryUrl() const - { - return getBaseUrl().withAppendedPath("service/jsonlivery.php"); - } - - CUrl CModelDataReader::getDistributorUrl() const - { - return getBaseUrl().withAppendedPath("service/jsondistributor.php"); - } - - CUrl CModelDataReader::getModelUrl() const - { - return getBaseUrl().withAppendedPath("service/jsonaircraftmodel.php"); - } - - const QString &CModelDataReader::parameterLatestTimestamp() - { - static const QString p("latestTimestamp"); - return p; - } - - const QString &CModelDataReader::parameterLatestId() - { - static const QString p("latestId"); - return p; - } - -} // namespace diff --git a/src/blackcore/modeldatareader.h b/src/blackcore/modeldatareader.h deleted file mode 100644 index e37f096d5..000000000 --- a/src/blackcore/modeldatareader.h +++ /dev/null @@ -1,169 +0,0 @@ -/* Copyright (C) 2015 - * swift project Community / Contributors - * - * This file is part of swift project. It is subject to the license terms in the LICENSE file found in the top-level - * directory of this distribution and at http://www.swift-project.org/license.html. No part of swift project, - * including this file, may be copied, modified, propagated, or distributed except according to the terms - * contained in the LICENSE file. - */ - -//! \file - -#ifndef BLACKCORE_MODELDATAREADER_H -#define BLACKCORE_MODELDATAREADER_H - -#include "blackcore/blackcoreexport.h" -#include "blackcore/databasereader.h" -#include "blackmisc/aviation/airlineicaocode.h" -#include "blackmisc/aviation/livery.h" -#include "blackmisc/aviation/liverylist.h" -#include "blackmisc/network/entityflags.h" -#include "blackmisc/network/url.h" -#include "blackmisc/simulation/aircraftmodel.h" -#include "blackmisc/simulation/aircraftmodellist.h" -#include "blackmisc/simulation/distributor.h" -#include "blackmisc/simulation/distributorlist.h" - -#include -#include -#include -#include -#include -#include - -class QNetworkReply; - -namespace BlackCore -{ - //! Read model related data from Database - class BLACKCORE_EXPORT CModelDataReader : public CDatabaseReader - { - Q_OBJECT - - public: - //! Constructor - explicit CModelDataReader(QObject *owner); - - //! Get aircraft liveries - //! \threadsafe - BlackMisc::Aviation::CLiveryList getLiveries() const; - - //! Get aircraft livery for code - //! \threadsafe - BlackMisc::Aviation::CLivery getLiveryForCombinedCode(const QString &combinedCode) const; - - //! Get aircraft livery for ICAO code - //! \threadsafe - BlackMisc::Aviation::CLivery getStdLiveryForAirlineCode(const BlackMisc::Aviation::CAirlineIcaoCode &icao) const; - - //! Get aircraft livery for id - //! \threadsafe - BlackMisc::Aviation::CLivery getLiveryForDbKey(int id) const; - - //! Best match specified by livery - //! \threadsafe - BlackMisc::Aviation::CLivery smartLiverySelector(const BlackMisc::Aviation::CLivery &livery) const; - - //! Get distributors (of models) - //! \threadsafe - BlackMisc::Simulation::CDistributorList getDistributors() const; - - //! Get models - //! \threadsafe - BlackMisc::Simulation::CAircraftModelList getModels() const; - - //! Get model for string - //! \threadsafe - BlackMisc::Simulation::CAircraftModel getModelForModelString(const QString &modelString) const; - - //! Get model for designator/combined code - //! \threadsafe - BlackMisc::Simulation::CAircraftModelList getModelsForAircraftDesignatorAndLiveryCombinedCode(const QString &aircraftDesignator, const QString &combinedCode); - - //! Get aircraft liveries count - //! \threadsafe - int getLiveriesCount() const; - - //! Get model distributors count - //! \threadsafe - int getDistributorsCount() const; - - //! Best match specified by distributor - //! \threadsafe - BlackMisc::Simulation::CDistributor smartDistributorSelector(const BlackMisc::Simulation::CDistributor &distributorPattern) const; - - //! Get models count - //! \threadsafe - int getModelsCount() const; - - //! Get model keys - //! \threadsafe - QList getModelDbKeys() const; - - //! Get model keys - //! \threadsafe - QStringList getModelStrings() const; - - //! All data read? - //! \threadsafe - bool areAllDataRead() const; - - //! Read to JSON file - bool readFromJsonFiles(const QString &dir, BlackMisc::Network::CEntityFlags::Entity whatToRead = BlackMisc::Network::CEntityFlags::DistributorLiveryModel); - - //! Read from static DB data file - bool readFromJsonFilesInBackground(const QString &dir, BlackMisc::Network::CEntityFlags::Entity whatToRead = BlackMisc::Network::CEntityFlags::DistributorLiveryModel); - - //! Write to JSON file - bool writeToJsonFiles(const QString &dir) const; - - //! Name of latest timestamp - static const QString ¶meterLatestTimestamp(); - - //! Name of latest timestamp - static const QString ¶meterLatestId(); - - signals: - //! Combined read signal - void dataRead(BlackMisc::Network::CEntityFlags::Entity entity, BlackMisc::Network::CEntityFlags::ReadState state, int number); - - private slots: - //! Liveries have been read - void ps_parseLiveryData(QNetworkReply *nwReply); - - //! Distributors have been read - void ps_parseDistributorData(QNetworkReply *nwReply); - - //! Models have been read - void ps_parseModelData(QNetworkReply *nwReply); - - //! Read / re-read data file - void ps_read(BlackMisc::Network::CEntityFlags::Entity entity = BlackMisc::Network::CEntityFlags::DistributorLiveryModel, const QDateTime &newerThan = QDateTime()); - - private: - BlackMisc::Aviation::CLiveryList m_liveries; - BlackMisc::Simulation::CDistributorList m_distributors; - BlackMisc::Simulation::CAircraftModelList m_models; - BlackMisc::Network::CUrl m_urlLiveries; - BlackMisc::Network::CUrl m_urlDistributors; - BlackMisc::Network::CUrl m_urlModels; - - mutable QReadWriteLock m_lockDistributor; - mutable QReadWriteLock m_lockLivery; - mutable QReadWriteLock m_lockModels; - - //! Base URL - BlackMisc::Network::CUrl getBaseUrl() const; - - //! URL livery web service - BlackMisc::Network::CUrl getLiveryUrl() const; - - //! URL distributor web service - BlackMisc::Network::CUrl getDistributorUrl() const; - - //! URL model web service - BlackMisc::Network::CUrl getModelUrl() const; - }; -} // ns - -#endif // guard diff --git a/src/blackmisc/datastore.cpp b/src/blackmisc/datastore.cpp deleted file mode 100644 index 18b499280..000000000 --- a/src/blackmisc/datastore.cpp +++ /dev/null @@ -1,204 +0,0 @@ -/* Copyright (C) 2015 - * swift project Community / Contributors - * - * This file is part of swift project. It is subject to the license terms in the LICENSE file found in the top-level - * directory of this distribution and at http://www.swift-project.org/license.html. No part of swift project, - * including this file, may be copied, modified, propagated, or distributed except according to the terms - * contained in the LICENSE file. - */ - -#include "blackmisc/datastore.h" -#include "blackmisc/comparefunctions.h" -#include "blackmisc/datastoreutility.h" -#include "blackmisc/icon.h" -#include "blackmisc/iconlist.h" -#include "blackmisc/icons.h" - -#include -#include - -namespace BlackMisc -{ - - QString IDatastoreObjectWithIntegerKey::getDbKeyAsString() const - { - if (this->m_dbKey < 0) { return ""; } - return QString::number(this->m_dbKey); - } - - QString IDatastoreObjectWithIntegerKey::getDbKeyAsStringInParentheses() const - { - if (this->m_dbKey < 0) { return ""; } - return "(" + QString::number(this->m_dbKey) + ")"; - } - - void IDatastoreObjectWithIntegerKey::setDbKey(const QString &key) - { - bool ok; - int k = key.toInt(&ok); - if (!ok) { k = -1; } - this->m_dbKey = k; - } - - const CIcon &IDatastoreObjectWithIntegerKey::toDatabaseIcon() const - { - static const CIcon empty; - if (this->hasValidDbKey()) { return CIconList::iconByIndex(CIcons::StandardIconDatabaseKey16); } - return empty; - } - - int IDatastoreObjectWithIntegerKey::stringToDbKey(const QString &candidate) - { - if (candidate.isEmpty()) { return invalidDbKey(); } - bool ok; - int k = candidate.toInt(&ok); - return ok ? k : invalidDbKey(); - } - - QJsonValue IDatastoreObjectWithIntegerKey::getDbKeyAsJsonValue() const - { - if (this->hasValidDbKey()) { return QJsonValue(this->m_dbKey); } - return QJsonValue(); - } - - void IDatastoreObjectWithIntegerKey::setKeyAndTimestampFromDatabaseJson(const QJsonObject &json, const QString &prefix) - { - int dbKey = json.value(prefix + "id").toInt(-1); - QString timestampString(json.value(prefix + "lastupdated").toString()); - QDateTime ts(CDatastoreUtility::parseTimestamp(timestampString)); - this->setDbKey(dbKey); - this->setUtcTimestamp(ts); - } - - bool IDatastoreObjectWithIntegerKey::existsKey(const QJsonObject &json, const QString &prefix) - { - return !json.value(prefix + "id").isNull(); - } - - CVariant IDatastoreObjectWithIntegerKey::propertyByIndex(const CPropertyIndex &index) const - { - if (ITimestampBased::canHandleIndex(index)) { return ITimestampBased::propertyByIndex(index); } - ColumnIndex i = index.frontCasted(); - switch (i) - { - case IndexDbIntegerKey: return CVariant::from(this->m_dbKey); - case IndexDatabaseIcon: return CVariant::from(this->toDatabaseIcon()); - default: break; - } - return CVariant(); - } - - void IDatastoreObjectWithIntegerKey::setPropertyByIndex(const CPropertyIndex &index, const CVariant &variant) - { - if (ITimestampBased::canHandleIndex(index)) { ITimestampBased::setPropertyByIndex(index, variant); return; } - ColumnIndex i = index.frontCasted(); - switch (i) - { - case IndexDbIntegerKey: - this->m_dbKey = variant.toInt(); - break; - default: - break; - } - } - - int IDatastoreObjectWithIntegerKey::comparePropertyByIndex(const CPropertyIndex &index, const IDatastoreObjectWithIntegerKey &compareValue) const - { - if (ITimestampBased::canHandleIndex(index)) { return ITimestampBased::comparePropertyByIndex(index, compareValue); } - const ColumnIndex i = index.frontCasted(); - switch (i) - { - case IndexDbIntegerKey: return Compare::compare(this->m_dbKey, compareValue.getDbKey()); - case IndexDatabaseIcon: return Compare::compare(this->hasValidDbKey(), compareValue.hasValidDbKey()); - default: break; - } - Q_ASSERT_X(false, Q_FUNC_INFO, "Compare failed"); - return 0; - } - - bool IDatastoreObjectWithIntegerKey::canHandleIndex(const BlackMisc::CPropertyIndex &index) - { - if (ITimestampBased::canHandleIndex(index)) { return true;} - int i = index.frontCasted(); - return (i >= static_cast(IndexDbIntegerKey)) && (i <= static_cast(IndexDatabaseIcon)); - } - - QJsonValue IDatastoreObjectWithStringKey::getDbKeyAsJsonValue() const - { - if (this->hasValidDbKey()) { return QJsonValue(this->m_dbKey); } - static const QJsonValue null; - return null; - } - - const CIcon &IDatastoreObjectWithStringKey::toDatabaseIcon() const - { - static const CIcon empty; - if (this->hasValidDbKey()) { return CIconList::iconByIndex(CIcons::StandardIconDatabaseKey16); } - return empty; - } - - void IDatastoreObjectWithStringKey::setKeyAndTimestampFromDatabaseJson(const QJsonObject &json, const QString &prefix) - { - QString dbKey = json.value(prefix + "id").toString(); - QDateTime ts(CDatastoreUtility::parseTimestamp(json.value(prefix + "lastupdated").toString())); - this->setDbKey(dbKey); - this->setUtcTimestamp(ts); - } - - bool IDatastoreObjectWithStringKey::existsKey(const QJsonObject &json, const QString &prefix) - { - return !json.value(prefix + "id").isNull(); - } - - CVariant IDatastoreObjectWithStringKey::propertyByIndex(const CPropertyIndex &index) const - { - if (ITimestampBased::canHandleIndex(index)) { return ITimestampBased::propertyByIndex(index); } - const ColumnIndex i = index.frontCasted(); - switch (i) - { - case IndexDbStringKey: return CVariant::from(this->m_dbKey); - case IndexDatabaseIcon: return CVariant::from(this->toDatabaseIcon()); - default: - break; - } - return CVariant(); - } - - void IDatastoreObjectWithStringKey::setPropertyByIndex(const CPropertyIndex &index, const CVariant &variant) - { - if (ITimestampBased::canHandleIndex(index)) { ITimestampBased::setPropertyByIndex(index, variant); return; } - const ColumnIndex i = index.frontCasted(); - switch (i) - { - case IndexDbStringKey: - this->m_dbKey = variant.value(); - break; - default: - break; - } - } - - int IDatastoreObjectWithStringKey::comparePropertyByIndex(const CPropertyIndex &index, const IDatastoreObjectWithStringKey &compareValue) const - { - if (ITimestampBased::canHandleIndex(index)) { return ITimestampBased::comparePropertyByIndex(index, compareValue); } - const ColumnIndex i = index.frontCasted(); - switch (i) - { - case IndexDbStringKey: return this->m_dbKey.compare(compareValue.getDbKey()); - case IndexDatabaseIcon: return Compare::compare(this->hasValidDbKey(), compareValue.hasValidDbKey()); - default: - break; - } - Q_ASSERT_X(false, Q_FUNC_INFO, "Compare failed"); - return 0; - } - - bool IDatastoreObjectWithStringKey::canHandleIndex(const CPropertyIndex &index) - { - if (index.isEmpty()) { return false; } - if (ITimestampBased::canHandleIndex(index)) { return true;} - int i = index.frontCasted(); - return (i >= static_cast(IndexDbStringKey)) && (i <= static_cast(IndexDatabaseIcon)); - } - -} // namespace diff --git a/src/blackmisc/datastore.h b/src/blackmisc/datastore.h deleted file mode 100644 index 2a0ba8a0a..000000000 --- a/src/blackmisc/datastore.h +++ /dev/null @@ -1,164 +0,0 @@ -/* Copyright (C) 2015 - * swift project Community / Contributors - * - * This file is part of swift project. It is subject to the license terms in the LICENSE file found in the top-level - * directory of this distribution and at http://www.swift-project.org/license.html. No part of swift project, - * including this file, may be copied, modified, propagated, or distributed except according to the terms - * contained in the LICENSE file. - */ - -//! \file - -#ifndef BLACKMISC_DATASTORE_H -#define BLACKMISC_DATASTORE_H - -#include "blackmisc/blackmiscexport.h" -#include "blackmisc/propertyindex.h" -#include "blackmisc/timestampbased.h" -#include "blackmisc/variant.h" - -#include -#include -#include -#include - -namespace BlackMisc -{ - class CIcon; - - /*! - * Class from which a derived class can inherit datastore-related functions. - */ - class BLACKMISC_EXPORT IDatastoreObjectWithIntegerKey : public ITimestampBased - { - public: - //! Property index - enum ColumnIndex - { - IndexDbIntegerKey = CPropertyIndex::GlobalIndexIDatastoreInteger, - IndexDatabaseIcon - }; - - //! Get DB key. - int getDbKey() const { return m_dbKey; } - - //! DB key as string - QString getDbKeyAsString() const; - - //! Key as JSON value, or null - QJsonValue getDbKeyAsJsonValue() const; - - //! Db ley in parentheses, e.g. "(3)" - QString getDbKeyAsStringInParentheses() const; - - //! Set the DB key - void setDbKey(int key) { m_dbKey = key; } - - //! DB key passed as string - void setDbKey(const QString &key); - - //! Has valid DB key - bool hasValidDbKey() const { return m_dbKey >= 0; } - - //! Database icon if this has valid key, otherwise empty - const CIcon &toDatabaseIcon() const; - - //! Invalid key - static int invalidDbKey() { return -1; } - - //! Convert string to DB key - static int stringToDbKey(const QString &candidate); - - protected: - //! Constructor - IDatastoreObjectWithIntegerKey() {} - - //! Constructor - IDatastoreObjectWithIntegerKey(int key) : m_dbKey(key) {} - - //! Set key and timestamp values - void setKeyAndTimestampFromDatabaseJson(const QJsonObject &json, const QString &prefix = QString()); - - //! Is a key available? - static bool existsKey(const QJsonObject &json, const QString &prefix = QString()); - - //! \copydoc BlackMisc::Mixin::Index::propertyByIndex - CVariant propertyByIndex(const BlackMisc::CPropertyIndex &index) const; - - //! \copydoc BlackMisc::Mixin::Index::setPropertyByIndex - void setPropertyByIndex(const BlackMisc::CPropertyIndex &index, const CVariant &variant); - - //! Compare by index - int comparePropertyByIndex(const CPropertyIndex &index, const IDatastoreObjectWithIntegerKey &compareValue) const; - - //! Can given index be handled? - static bool canHandleIndex(const BlackMisc::CPropertyIndex &index); - - int m_dbKey = -1; //!< key - }; - - /*! - * Class from which a derived class can inherit datastore-related functions. - */ - class BLACKMISC_EXPORT IDatastoreObjectWithStringKey : public ITimestampBased - { - public: - //! Property index - enum ColumnIndex - { - IndexDbStringKey = CPropertyIndex::GlobalIndexIDatastoreString, - IndexDatabaseIcon - }; - - //! Get DB key. - const QString &getDbKey() const { return m_dbKey; } - - //! Key as JSON value, or null - QJsonValue getDbKeyAsJsonValue() const; - - //! Set the DB key - void setDbKey(const QString &key) { m_dbKey = key.trimmed().toUpper(); } - - //! Has valid DB key - bool hasValidDbKey() const { return !m_dbKey.isEmpty(); } - - //! Database icon if this has valid key, otherwise empty - const CIcon &toDatabaseIcon() const; - - //! Invalid key - static QString invalidDbKey() { return ""; } - - protected: - //! Constructor - IDatastoreObjectWithStringKey() {} - - //! Constructor - IDatastoreObjectWithStringKey(const QString &key) : m_dbKey(key) {} - - //! Set key and timestamp values - void setKeyAndTimestampFromDatabaseJson(const QJsonObject &json, const QString &prefix = QString()); - - //! Is a key available? - static bool existsKey(const QJsonObject &json, const QString &prefix = QString()); - - //! \copydoc BlackMisc::Mixin::Index::propertyByIndex - CVariant propertyByIndex(const BlackMisc::CPropertyIndex &index) const; - - //! \copydoc BlackMisc::Mixin::Index::setPropertyByIndex - void setPropertyByIndex(const BlackMisc::CPropertyIndex &index, const CVariant &variant); - - //! Compare by index - int comparePropertyByIndex(const BlackMisc::CPropertyIndex &index, const IDatastoreObjectWithStringKey &compareValue) const; - - //! Can given index be handled - static bool canHandleIndex(const BlackMisc::CPropertyIndex &index); - - QString m_dbKey; //!< key - }; - -} // namespace - -Q_DECLARE_INTERFACE(BlackMisc::IDatastoreObjectWithIntegerKey, "org.swift-project.blackmisc.idatastoreobjectwithintegerkey") -Q_DECLARE_INTERFACE(BlackMisc::IDatastoreObjectWithStringKey, "org.swift-project.blackmisc.idatastoreobjectwithstringkey") - -#endif // guard diff --git a/src/blackmisc/datastoreobjectlist.cpp b/src/blackmisc/datastoreobjectlist.cpp deleted file mode 100644 index c3b105458..000000000 --- a/src/blackmisc/datastoreobjectlist.cpp +++ /dev/null @@ -1,169 +0,0 @@ -/* Copyright (C) 2015 - * swift Project Community / Contributors - * - * This file is part of swift project. It is subject to the license terms in the LICENSE file found in the top-level - * directory of this distribution and at http://www.swift-project.org/license.html. No part of swift project, - * including this file, may be copied, modified, propagated, or distributed except according to the terms - * contained in the LICENSE file. - */ - -#include "blackmisc/datastoreobjectlist.h" -#include "blackmisc/predicates.h" -#include "blackmisc/countrylist.h" -#include "blackmisc/aviation/liverylist.h" -#include "blackmisc/aviation/aircrafticaocodelist.h" -#include "blackmisc/aviation/airlineicaocodelist.h" -#include "blackmisc/simulation/aircraftmodellist.h" -#include "blackmisc/simulation/distributorlist.h" - -#include -#include -#include -#include - -namespace BlackMisc -{ - template - IDatastoreObjectList::IDatastoreObjectList() - { - constexpr bool hasIntegerKey = std::is_base_of::value && std::is_same::value; - constexpr bool hasStringKey = std::is_base_of::value && std::is_base_of::value; - static_assert(hasIntegerKey || hasStringKey, "ObjectType needs to implement IDatastoreObjectWithXXXXKey and have appropriate KeyType"); - } - - template - OBJ IDatastoreObjectList::findByKey(KEYTYPE key, const OBJ ¬Found) const - { - return this->container().findFirstByOrDefault(&OBJ::getDbKey, key, notFound); - } - - template - OBJ IDatastoreObjectList::maxKeyObject() const - { - if (this->container().isEmpty()) { return OBJ(); } - const OBJ max = *std::max_element(this->container().begin(), this->container().end(), [](const OBJ & obj1, const OBJ & obj2) - { - bool v1 = obj1.hasValidDbKey(); - bool v2 = obj2.hasValidDbKey(); - if (v1 && v2) - { - return obj1.getDbKey() < obj2.getDbKey(); - } - return v2; - }); - return max; - } - - template - void IDatastoreObjectList::sortByKey() - { - this->container().sort(BlackMisc::Predicates::MemberLess(&OBJ::getDbKey)); - } - - template - QList IDatastoreObjectList::toDbKeyList() const - { - QList keys; - for (const OBJ &obj : ITimestampObjectList::container()) - { - if (!obj.hasValidDbKey()) { continue; } - keys.append(obj.getDbKey()); - } - return keys; - } - - template - KEYTYPE IDatastoreObjectList::getMaxKey(bool *ok) const - { - QList keys(this->toDbKeyList()); - if (keys.isEmpty()) - { - if (ok) { *ok = false; } - return KEYTYPE(); - } - KEYTYPE max = *std::max_element(keys.begin(), keys.end()); - if (ok) { *ok = true; } - return max; - } - - template - int IDatastoreObjectList::removeObjectsWithKeys(const QList &keys) - { - if (keys.isEmpty()) { return 0; } - if (this->container().isEmpty()) { return 0; } - CONTAINER newValues; - for (const OBJ &obj : ITimestampObjectList::container()) - { - if (keys.contains(obj.getDbKey())) { continue; } - newValues.push_back(obj); - } - int delta = this->container().size() - newValues.size(); - this->container() = newValues; - return delta; - } - - template - int IDatastoreObjectList::removeObjectsWithoutDbKey() - { - if (this->container().isEmpty()) { return 0; } - CONTAINER newValues; - for (const OBJ &obj : ITimestampObjectList::container()) - { - if (!obj.hasValidDbKey()) { continue; } - newValues.push_back(obj); - } - int delta = this->container().size() - newValues.size(); - this->container() = newValues; - return delta; - } - - template - int IDatastoreObjectList::replaceOrAddObjectsByKey(const CONTAINER &container) - { - if (container.isEmpty()) { return 0; } - if (this->container().isEmpty()) - { - this->container() = container; - return this->container().size(); - } - CONTAINER newValues(this->container()); - const QList keys(container.toDbKeyList()); - newValues.removeObjectsWithKeys(keys); - int removeSize = newValues.size(); // size after removing data - newValues.push_back(container); - this->container() = newValues; - return newValues.size() - removeSize; - } - - template - CONTAINER IDatastoreObjectList::fromDatabaseJson(const QJsonArray &array) - { - CONTAINER container; - for (const QJsonValue &value : array) - { - container.push_back(OBJ::fromDatabaseJson(value.toObject())); - } - return container; - } - - template - CONTAINER IDatastoreObjectList::fromDatabaseJson(const QJsonArray &array, const QString &prefix) - { - CONTAINER container; - for (const QJsonValue &value : array) - { - container.push_back(OBJ::fromDatabaseJson(value.toObject(), prefix)); - } - return container; - } - - // see here for the reason of thess forward instantiations - // http://www.parashift.com/c++-faq/separate-template-class-defn-from-decl.html - template class IDatastoreObjectList; - template class IDatastoreObjectList; - template class IDatastoreObjectList; - template class IDatastoreObjectList; - template class IDatastoreObjectList; - template class IDatastoreObjectList; - -} // namespace diff --git a/src/blackmisc/datastoreobjectlist.h b/src/blackmisc/datastoreobjectlist.h deleted file mode 100644 index 65dd514d4..000000000 --- a/src/blackmisc/datastoreobjectlist.h +++ /dev/null @@ -1,88 +0,0 @@ -/* Copyright (C) 2015 - * swift project Community / Contributors - * - * This file is part of swift project. It is subject to the license terms in the LICENSE file found in the top-level - * directory of this distribution and at http://www.swift-project.org/license.html. No part of swift project, - * including this file, may be copied, modified, propagated, or distributed except according to the terms - * contained in the LICENSE file. - */ - -//! \file - -#ifndef BLACKMISC_DATABASEOBJECTLIST_H -#define BLACKMISC_DATABASEOBJECTLIST_H - -#include "blackmisc/timestampobjectlist.h" - -#include -#include -#include - -namespace BlackMisc -{ - //! List of objects read from database. - //! Such objects should implement \sa ITimestampBased and \sa IDatastoreObjectWithIntegerKey or \sa IDatastoreObjectWithStringKey - template class IDatastoreObjectList : public ITimestampObjectList - { - public: - //! Object with key, notFound otherwise - OBJ findByKey(KEYTYPE key, const OBJ ¬Found = OBJ()) const; - - //! Object with max.key - OBJ maxKeyObject() const; - - //! Sort by timestamp - void sortByKey(); - - //! All keys as list - QList toDbKeyList() const; - - //! Max.key value (making sense with integer key) - KEYTYPE getMaxKey(bool *ok = nullptr) const; - - //! Remove objects with key - int removeObjectsWithKeys(const QList &keys); - - //! Remove objects without key - int removeObjectsWithoutDbKey(); - - //! Update or insert data (based on DB key) - int replaceOrAddObjectsByKey(const CONTAINER &container); - - //! From DB JSON with default prefixes - static CONTAINER fromDatabaseJson(const QJsonArray &array); - - //! From DB JSON - static CONTAINER fromDatabaseJson(const QJsonArray &array, const QString &prefix); - - protected: - //! Constructor - IDatastoreObjectList(); - }; - - //! \cond PRIVATE - namespace Aviation - { - class CLivery; - class CLiveryList; - } - - namespace Simulation - { - class CDistributor; - class CDistributorList; - class CAircraftModel; - class CAircraftModelList; - } - - extern template class BLACKMISC_EXPORT_TEMPLATE IDatastoreObjectList; - extern template class BLACKMISC_EXPORT_TEMPLATE IDatastoreObjectList; - extern template class BLACKMISC_EXPORT_TEMPLATE IDatastoreObjectList; - extern template class BLACKMISC_EXPORT_TEMPLATE IDatastoreObjectList; - extern template class BLACKMISC_EXPORT_TEMPLATE IDatastoreObjectList; - extern template class BLACKMISC_EXPORT_TEMPLATE IDatastoreObjectList; - //! \endcond - -} //namespace - -#endif //guard diff --git a/src/blackmisc/datastoreutility.cpp b/src/blackmisc/datastoreutility.cpp deleted file mode 100644 index 1e925ac64..000000000 --- a/src/blackmisc/datastoreutility.cpp +++ /dev/null @@ -1,148 +0,0 @@ -/* Copyright (C) 2015 - * swift project Community / Contributors - * - * This file is part of swift project. It is subject to the license terms in the LICENSE file found in the top-level - * directory of this distribution and at http://www.swift-project.org/license.html. No part of swift project, - * including this file, may be copied, modified, propagated, or distributed except according to the terms - * contained in the LICENSE file. - */ - -#include "blackmisc/datastoreutility.h" -#include "blackmisc/logcategory.h" -#include "blackmisc/logcategorylist.h" -#include "blackmisc/simulation/aircraftmodellist.h" -#include "blackmisc/statusmessage.h" -#include "blackmisc/statusmessagelist.h" -#include "blackmisc/stringutils.h" - -#include -#include -#include -#include -#include -#include - -using namespace BlackMisc; -using namespace BlackMisc::Simulation; - -namespace BlackMisc -{ - bool CDatastoreUtility::dbBoolStringToBool(const QString &dbBool) - { - return BlackMisc::stringToBool(dbBool); - } - - const QString &CDatastoreUtility::boolToDbYN(bool v) - { - static const QString y("Y"); - static const QString n("N"); - return v ? y : n; - } - - int CDatastoreUtility::extractIntegerKey(const QString &stringWithKey) - { - QString ks(stringWithKey.trimmed()); - if (ks.isEmpty()) { return -1; } - bool ok = false; - int key = ks.toInt(&ok); - if (ok) { return key; } // only a number - - // key in string with () - int i1 = ks.lastIndexOf('('); - if (i1 < 0) { return -1; } - int i2 = ks.lastIndexOf(')'); - if (i2 <= i1 + 1) { return -1;} - QString n(ks.mid(i1 + 1, i2 - i1 - 1)); - ok = false; - key = n.toInt(&ok); - return ok ? key : -1; - } - - QDateTime CDatastoreUtility::parseTimestamp(const QString ×tamp) - { - if (!timestamp.isEmpty()) - { - QString ts(timestamp.trimmed().remove(' ').remove('-').remove(':')); // normalize - QDateTime dt = QDateTime::fromString(ts, "yyyyMMddHHmmss"); - dt.setTimeZone(QTimeZone::utc()); - return dt; - } - else - { - return QDateTime(); - } - } - - bool CDatastoreUtility::parseSwiftPublishResponse(const QString &jsonResponse, CAircraftModelList &publishedModels, CAircraftModelList &skippedModels, CStatusMessageList &messages) - { - static const CLogCategoryList cats({ CLogCategory::swiftDbWebservice()}); - if (jsonResponse.isEmpty()) - { - messages.push_back(CStatusMessage(cats, CStatusMessage::SeverityError, "Empty JSON data")); - return false; - } - - QJsonDocument jsonDoc(QJsonDocument::fromJson(jsonResponse.toUtf8())); - - // array of messages - if (jsonDoc.isArray()) - { - CStatusMessageList msgs(CStatusMessageList::fromDatabaseJson(jsonDoc.array())); - messages.push_back(msgs); - return true; - } - - // no object -> most likely some fucked up HTML string with the PHP error - if (!jsonDoc.isObject()) - { - QString phpError(jsonResponse); - phpError.remove(QRegExp("<[^>]*>")); - messages.push_back(CStatusMessage(cats, CStatusMessage::SeverityError, phpError)); - return false; - } - - // fully blown object - QJsonObject json(jsonDoc.object()); - bool data = false; - if (json.contains("msgs")) - { - QJsonValue msgJson(json.take("msgs")); - CStatusMessageList msgs(CStatusMessageList::fromDatabaseJson(msgJson.toArray())); - if (!msgs.isEmpty()) - { - messages.push_back(msgs); - data = true; - } - } - - if (json.contains("publishedModels")) - { - QJsonValue publishedJson(json.take("publishedModels")); - CAircraftModelList published = CAircraftModelList::fromDatabaseJson(publishedJson.toArray(), ""); - if (!published.isEmpty()) - { - publishedModels.push_back(published); - data = true; - } - } - - if (json.contains("skippedModels")) - { - QJsonValue skippedJson(json.take("skippedModels")); - CAircraftModelList skipped = CAircraftModelList::fromDatabaseJson(skippedJson.toArray(), ""); - if (!skipped.isEmpty()) - { - skippedModels.push_back(skipped); - data = true; - } - } - - if (!data) - { - messages.push_back(CStatusMessage(cats, CStatusMessage::SeverityError, "Received response, but no JSON data")); - } - - return data; - } - -} // namespace diff --git a/src/blackmisc/datastoreutility.h b/src/blackmisc/datastoreutility.h deleted file mode 100644 index 96717fab5..000000000 --- a/src/blackmisc/datastoreutility.h +++ /dev/null @@ -1,53 +0,0 @@ -/* Copyright (C) 2015 - * swift project Community / Contributors - * - * This file is part of swift project. It is subject to the license terms in the LICENSE file found in the top-level - * directory of this distribution and at http://www.swift-project.org/license.html. No part of swift project, - * including this file, may be copied, modified, propagated, or distributed except according to the terms - * contained in the LICENSE file. - */ - -//! \file - -#ifndef BLACKMISC_DATASTOREUTILITY_H -#define BLACKMISC_DATASTOREUTILITY_H - -#include "blackmisc/blackmiscexport.h" - -#include -#include - -namespace BlackMisc -{ - class CStatusMessageList; - - namespace Simulation { class CAircraftModelList; } - - /*! - * Class with datastore related utilities - */ - class BLACKMISC_EXPORT CDatastoreUtility - { - public: - //! No constructor - CDatastoreUtility() = delete; - - //! DB Bool value to bool - static bool dbBoolStringToBool(const QString &dbBool); - - //! Bool to DB yes/no - static const QString &boolToDbYN(bool v); - - //! Extract key from string like "MyAircraft (33)" - static int extractIntegerKey(const QString &stringWithKey); - - //! Parse a timestamp object - static QDateTime parseTimestamp(const QString ×tamp); - - //! Get data from a DB response - static bool parseSwiftPublishResponse(const QString &jsonResponse, BlackMisc::Simulation::CAircraftModelList &publishedModels, BlackMisc::Simulation::CAircraftModelList &skippedModels, BlackMisc::CStatusMessageList &messages); - }; - -} // namespace - -#endif // guard diff --git a/src/blackmisc/db/datastore.cpp b/src/blackmisc/db/datastore.cpp new file mode 100644 index 000000000..41d85e0ce --- /dev/null +++ b/src/blackmisc/db/datastore.cpp @@ -0,0 +1,205 @@ +/* Copyright (C) 2015 + * swift project Community / Contributors + * + * This file is part of swift project. It is subject to the license terms in the LICENSE file found in the top-level + * directory of this distribution and at http://www.swift-project.org/license.html. No part of swift project, + * including this file, may be copied, modified, propagated, or distributed except according to the terms + * contained in the LICENSE file. + */ + +#include "blackmisc/db/datastore.h" +#include "blackmisc/comparefunctions.h" +#include "blackmisc/db/datastoreutility.h" +#include "blackmisc/icon.h" +#include "blackmisc/iconlist.h" +#include "blackmisc/icons.h" + +#include +#include + +namespace BlackMisc +{ + namespace Db + { + QString IDatastoreObjectWithIntegerKey::getDbKeyAsString() const + { + if (this->m_dbKey < 0) { return ""; } + return QString::number(this->m_dbKey); + } + + QString IDatastoreObjectWithIntegerKey::getDbKeyAsStringInParentheses() const + { + if (this->m_dbKey < 0) { return ""; } + return "(" + QString::number(this->m_dbKey) + ")"; + } + + void IDatastoreObjectWithIntegerKey::setDbKey(const QString &key) + { + bool ok; + int k = key.toInt(&ok); + if (!ok) { k = -1; } + this->m_dbKey = k; + } + + const CIcon &IDatastoreObjectWithIntegerKey::toDatabaseIcon() const + { + static const CIcon empty; + if (this->hasValidDbKey()) { return CIconList::iconByIndex(CIcons::StandardIconDatabaseKey16); } + return empty; + } + + int IDatastoreObjectWithIntegerKey::stringToDbKey(const QString &candidate) + { + if (candidate.isEmpty()) { return invalidDbKey(); } + bool ok; + int k = candidate.toInt(&ok); + return ok ? k : invalidDbKey(); + } + + QJsonValue IDatastoreObjectWithIntegerKey::getDbKeyAsJsonValue() const + { + if (this->hasValidDbKey()) { return QJsonValue(this->m_dbKey); } + return QJsonValue(); + } + + void IDatastoreObjectWithIntegerKey::setKeyAndTimestampFromDatabaseJson(const QJsonObject &json, const QString &prefix) + { + int dbKey = json.value(prefix + "id").toInt(-1); + const QString timestampString(json.value(prefix + "lastupdated").toString()); + const QDateTime ts(CDatastoreUtility::parseTimestamp(timestampString)); + this->setDbKey(dbKey); + this->setUtcTimestamp(ts); + } + + bool IDatastoreObjectWithIntegerKey::existsKey(const QJsonObject &json, const QString &prefix) + { + return !json.value(prefix + "id").isNull(); + } + + CVariant IDatastoreObjectWithIntegerKey::propertyByIndex(const CPropertyIndex &index) const + { + if (ITimestampBased::canHandleIndex(index)) { return ITimestampBased::propertyByIndex(index); } + ColumnIndex i = index.frontCasted(); + switch (i) + { + case IndexDbIntegerKey: return CVariant::from(this->m_dbKey); + case IndexDatabaseIcon: return CVariant::from(this->toDatabaseIcon()); + default: break; + } + return CVariant(); + } + + void IDatastoreObjectWithIntegerKey::setPropertyByIndex(const CPropertyIndex &index, const CVariant &variant) + { + if (ITimestampBased::canHandleIndex(index)) { ITimestampBased::setPropertyByIndex(index, variant); return; } + ColumnIndex i = index.frontCasted(); + switch (i) + { + case IndexDbIntegerKey: + this->m_dbKey = variant.toInt(); + break; + default: + break; + } + } + + int IDatastoreObjectWithIntegerKey::comparePropertyByIndex(const CPropertyIndex &index, const IDatastoreObjectWithIntegerKey &compareValue) const + { + if (ITimestampBased::canHandleIndex(index)) { return ITimestampBased::comparePropertyByIndex(index, compareValue); } + const ColumnIndex i = index.frontCasted(); + switch (i) + { + case IndexDbIntegerKey: return Compare::compare(this->m_dbKey, compareValue.getDbKey()); + case IndexDatabaseIcon: return Compare::compare(this->hasValidDbKey(), compareValue.hasValidDbKey()); + default: break; + } + Q_ASSERT_X(false, Q_FUNC_INFO, "Compare failed"); + return 0; + } + + bool IDatastoreObjectWithIntegerKey::canHandleIndex(const BlackMisc::CPropertyIndex &index) + { + if (ITimestampBased::canHandleIndex(index)) { return true;} + int i = index.frontCasted(); + return (i >= static_cast(IndexDbIntegerKey)) && (i <= static_cast(IndexDatabaseIcon)); + } + + QJsonValue IDatastoreObjectWithStringKey::getDbKeyAsJsonValue() const + { + if (this->hasValidDbKey()) { return QJsonValue(this->m_dbKey); } + static const QJsonValue null; + return null; + } + + const CIcon &IDatastoreObjectWithStringKey::toDatabaseIcon() const + { + static const CIcon empty; + if (this->hasValidDbKey()) { return CIconList::iconByIndex(CIcons::StandardIconDatabaseKey16); } + return empty; + } + + void IDatastoreObjectWithStringKey::setKeyAndTimestampFromDatabaseJson(const QJsonObject &json, const QString &prefix) + { + QString dbKey = json.value(prefix + "id").toString(); + QDateTime ts(CDatastoreUtility::parseTimestamp(json.value(prefix + "lastupdated").toString())); + this->setDbKey(dbKey); + this->setUtcTimestamp(ts); + } + + bool IDatastoreObjectWithStringKey::existsKey(const QJsonObject &json, const QString &prefix) + { + return !json.value(prefix + "id").isNull(); + } + + CVariant IDatastoreObjectWithStringKey::propertyByIndex(const CPropertyIndex &index) const + { + if (ITimestampBased::canHandleIndex(index)) { return ITimestampBased::propertyByIndex(index); } + const ColumnIndex i = index.frontCasted(); + switch (i) + { + case IndexDbStringKey: return CVariant::from(this->m_dbKey); + case IndexDatabaseIcon: return CVariant::from(this->toDatabaseIcon()); + default: + break; + } + return CVariant(); + } + + void IDatastoreObjectWithStringKey::setPropertyByIndex(const CPropertyIndex &index, const CVariant &variant) + { + if (ITimestampBased::canHandleIndex(index)) { ITimestampBased::setPropertyByIndex(index, variant); return; } + const ColumnIndex i = index.frontCasted(); + switch (i) + { + case IndexDbStringKey: + this->m_dbKey = variant.value(); + break; + default: + break; + } + } + + int IDatastoreObjectWithStringKey::comparePropertyByIndex(const CPropertyIndex &index, const IDatastoreObjectWithStringKey &compareValue) const + { + if (ITimestampBased::canHandleIndex(index)) { return ITimestampBased::comparePropertyByIndex(index, compareValue); } + const ColumnIndex i = index.frontCasted(); + switch (i) + { + case IndexDbStringKey: return this->m_dbKey.compare(compareValue.getDbKey()); + case IndexDatabaseIcon: return Compare::compare(this->hasValidDbKey(), compareValue.hasValidDbKey()); + default: + break; + } + Q_ASSERT_X(false, Q_FUNC_INFO, "Compare failed"); + return 0; + } + + bool IDatastoreObjectWithStringKey::canHandleIndex(const CPropertyIndex &index) + { + if (index.isEmpty()) { return false; } + if (ITimestampBased::canHandleIndex(index)) { return true;} + int i = index.frontCasted(); + return (i >= static_cast(IndexDbStringKey)) && (i <= static_cast(IndexDatabaseIcon)); + } + } +} // namespace diff --git a/src/blackmisc/db/datastore.h b/src/blackmisc/db/datastore.h new file mode 100644 index 000000000..5c9649548 --- /dev/null +++ b/src/blackmisc/db/datastore.h @@ -0,0 +1,166 @@ +/* Copyright (C) 2015 + * swift project Community / Contributors + * + * This file is part of swift project. It is subject to the license terms in the LICENSE file found in the top-level + * directory of this distribution and at http://www.swift-project.org/license.html. No part of swift project, + * including this file, may be copied, modified, propagated, or distributed except according to the terms + * contained in the LICENSE file. + */ + +//! \file + +#ifndef BLACKMISC_DB_DATASTORE_H +#define BLACKMISC_DB_DATASTORE_H + +#include "blackmisc/blackmiscexport.h" +#include "blackmisc/propertyindex.h" +#include "blackmisc/timestampbased.h" +#include "blackmisc/variant.h" + +#include +#include +#include +#include + +namespace BlackMisc +{ + class CIcon; + + namespace Db + { + /*! + * Class from which a derived class can inherit datastore-related functions. + */ + class BLACKMISC_EXPORT IDatastoreObjectWithIntegerKey : public ITimestampBased + { + public: + //! Property index + enum ColumnIndex + { + IndexDbIntegerKey = CPropertyIndex::GlobalIndexIDatastoreInteger, + IndexDatabaseIcon + }; + + //! Get DB key. + int getDbKey() const { return m_dbKey; } + + //! DB key as string + QString getDbKeyAsString() const; + + //! Key as JSON value, or null + QJsonValue getDbKeyAsJsonValue() const; + + //! Db ley in parentheses, e.g. "(3)" + QString getDbKeyAsStringInParentheses() const; + + //! Set the DB key + void setDbKey(int key) { m_dbKey = key; } + + //! DB key passed as string + void setDbKey(const QString &key); + + //! Has valid DB key + bool hasValidDbKey() const { return m_dbKey >= 0; } + + //! Database icon if this has valid key, otherwise empty + const CIcon &toDatabaseIcon() const; + + //! Invalid key + static int invalidDbKey() { return -1; } + + //! Convert string to DB key + static int stringToDbKey(const QString &candidate); + + protected: + //! Constructor + IDatastoreObjectWithIntegerKey() {} + + //! Constructor + IDatastoreObjectWithIntegerKey(int key) : m_dbKey(key) {} + + //! Set key and timestamp values + void setKeyAndTimestampFromDatabaseJson(const QJsonObject &json, const QString &prefix = QString()); + + //! Is a key available? + static bool existsKey(const QJsonObject &json, const QString &prefix = QString()); + + //! \copydoc BlackMisc::Mixin::Index::propertyByIndex + CVariant propertyByIndex(const BlackMisc::CPropertyIndex &index) const; + + //! \copydoc BlackMisc::Mixin::Index::setPropertyByIndex + void setPropertyByIndex(const BlackMisc::CPropertyIndex &index, const CVariant &variant); + + //! Compare by index + int comparePropertyByIndex(const CPropertyIndex &index, const IDatastoreObjectWithIntegerKey &compareValue) const; + + //! Can given index be handled? + static bool canHandleIndex(const BlackMisc::CPropertyIndex &index); + + int m_dbKey = -1; //!< key + }; + + /*! + * Class from which a derived class can inherit datastore-related functions. + */ + class BLACKMISC_EXPORT IDatastoreObjectWithStringKey : public ITimestampBased + { + public: + //! Property index + enum ColumnIndex + { + IndexDbStringKey = CPropertyIndex::GlobalIndexIDatastoreString, + IndexDatabaseIcon + }; + + //! Get DB key. + const QString &getDbKey() const { return m_dbKey; } + + //! Key as JSON value, or null + QJsonValue getDbKeyAsJsonValue() const; + + //! Set the DB key + void setDbKey(const QString &key) { m_dbKey = key.trimmed().toUpper(); } + + //! Has valid DB key + bool hasValidDbKey() const { return !m_dbKey.isEmpty(); } + + //! Database icon if this has valid key, otherwise empty + const CIcon &toDatabaseIcon() const; + + //! Invalid key + static QString invalidDbKey() { return ""; } + + protected: + //! Constructor + IDatastoreObjectWithStringKey() {} + + //! Constructor + IDatastoreObjectWithStringKey(const QString &key) : m_dbKey(key) {} + + //! Set key and timestamp values + void setKeyAndTimestampFromDatabaseJson(const QJsonObject &json, const QString &prefix = QString()); + + //! Is a key available? + static bool existsKey(const QJsonObject &json, const QString &prefix = QString()); + + //! \copydoc BlackMisc::Mixin::Index::propertyByIndex + CVariant propertyByIndex(const BlackMisc::CPropertyIndex &index) const; + + //! \copydoc BlackMisc::Mixin::Index::setPropertyByIndex + void setPropertyByIndex(const BlackMisc::CPropertyIndex &index, const CVariant &variant); + + //! Compare by index + int comparePropertyByIndex(const BlackMisc::CPropertyIndex &index, const IDatastoreObjectWithStringKey &compareValue) const; + + //! Can given index be handled + static bool canHandleIndex(const BlackMisc::CPropertyIndex &index); + + QString m_dbKey; //!< key + }; + } // ns +} // ns + +Q_DECLARE_INTERFACE(BlackMisc::Db::IDatastoreObjectWithIntegerKey, "org.swift-project.blackmisc.db.idatastoreobjectwithintegerkey") +Q_DECLARE_INTERFACE(BlackMisc::Db::IDatastoreObjectWithStringKey, "org.swift-project.blackmisc.db.idatastoreobjectwithstringkey") + +#endif // guard diff --git a/src/blackmisc/db/datastoreobjectlist.cpp b/src/blackmisc/db/datastoreobjectlist.cpp new file mode 100644 index 000000000..0030e36ed --- /dev/null +++ b/src/blackmisc/db/datastoreobjectlist.cpp @@ -0,0 +1,175 @@ +/* Copyright (C) 2015 + * swift Project Community / Contributors + * + * This file is part of swift project. It is subject to the license terms in the LICENSE file found in the top-level + * directory of this distribution and at http://www.swift-project.org/license.html. No part of swift project, + * including this file, may be copied, modified, propagated, or distributed except according to the terms + * contained in the LICENSE file. + */ + +#include "blackmisc/db/datastoreobjectlist.h" +#include "blackmisc/predicates.h" +#include "blackmisc/countrylist.h" +#include "blackmisc/aviation/liverylist.h" +#include "blackmisc/aviation/aircrafticaocodelist.h" +#include "blackmisc/aviation/airlineicaocodelist.h" +#include "blackmisc/db/dbinfolist.h" +#include "blackmisc/simulation/aircraftmodellist.h" +#include "blackmisc/simulation/distributorlist.h" + +#include +#include +#include +#include +#include + +namespace BlackMisc +{ + namespace Db + { + template + IDatastoreObjectList::IDatastoreObjectList() + { + constexpr bool hasIntegerKey = std::is_base_of::value && std::is_same::value; + constexpr bool hasStringKey = std::is_base_of::value && std::is_base_of::value; + static_assert(hasIntegerKey || hasStringKey, "ObjectType needs to implement IDatastoreObjectWithXXXXKey and have appropriate KeyType"); + } + + template + OBJ IDatastoreObjectList::findByKey(KEYTYPE key, const OBJ ¬Found) const + { + return this->container().findFirstByOrDefault(&OBJ::getDbKey, key, notFound); + } + + template + OBJ IDatastoreObjectList::maxKeyObject() const + { + if (this->container().isEmpty()) { return OBJ(); } + const OBJ max = *std::max_element(this->container().begin(), this->container().end(), [](const OBJ & obj1, const OBJ & obj2) + { + bool v1 = obj1.hasValidDbKey(); + bool v2 = obj2.hasValidDbKey(); + if (v1 && v2) + { + return obj1.getDbKey() < obj2.getDbKey(); + } + return v2; + }); + return max; + } + + template + void IDatastoreObjectList::sortByKey() + { + this->container().sort(BlackMisc::Predicates::MemberLess(&OBJ::getDbKey)); + } + + template + QList IDatastoreObjectList::toDbKeyList() const + { + QList keys; + for (const OBJ &obj : ITimestampObjectList::container()) + { + if (!obj.hasValidDbKey()) { continue; } + keys.append(obj.getDbKey()); + } + return keys; + } + + template + KEYTYPE IDatastoreObjectList::getMaxKey(bool *ok) const + { + QList keys(this->toDbKeyList()); + if (keys.isEmpty()) + { + if (ok) { *ok = false; } + return KEYTYPE(); + } + KEYTYPE max = *std::max_element(keys.begin(), keys.end()); + if (ok) { *ok = true; } + return max; + } + + template + int IDatastoreObjectList::removeObjectsWithKeys(const QList &keys) + { + if (keys.isEmpty()) { return 0; } + if (this->container().isEmpty()) { return 0; } + CONTAINER newValues; + for (const OBJ &obj : ITimestampObjectList::container()) + { + if (keys.contains(obj.getDbKey())) { continue; } + newValues.push_back(obj); + } + int delta = this->container().size() - newValues.size(); + this->container() = newValues; + return delta; + } + + template + int IDatastoreObjectList::removeObjectsWithoutDbKey() + { + if (this->container().isEmpty()) { return 0; } + CONTAINER newValues; + for (const OBJ &obj : ITimestampObjectList::container()) + { + if (!obj.hasValidDbKey()) { continue; } + newValues.push_back(obj); + } + int delta = this->container().size() - newValues.size(); + this->container() = newValues; + return delta; + } + + template + int IDatastoreObjectList::replaceOrAddObjectsByKey(const CONTAINER &container) + { + if (container.isEmpty()) { return 0; } + if (this->container().isEmpty()) + { + this->container() = container; + return this->container().size(); + } + CONTAINER newValues(this->container()); + const QList keys(container.toDbKeyList()); + newValues.removeObjectsWithKeys(keys); + int removeSize = newValues.size(); // size after removing data + newValues.push_back(container); + this->container() = newValues; + return newValues.size() - removeSize; + } + + template + CONTAINER IDatastoreObjectList::fromDatabaseJson(const QJsonArray &array) + { + CONTAINER container; + for (const QJsonValue &value : array) + { + container.push_back(OBJ::fromDatabaseJson(value.toObject())); + } + return container; + } + + template + CONTAINER IDatastoreObjectList::fromDatabaseJson(const QJsonArray &array, const QString &prefix) + { + CONTAINER container; + for (const QJsonValue &value : array) + { + container.push_back(OBJ::fromDatabaseJson(value.toObject(), prefix)); + } + return container; + } + + // see here for the reason of thess forward instantiations + // http://www.parashift.com/c++-faq/separate-template-class-defn-from-decl.html + template class IDatastoreObjectList; + template class IDatastoreObjectList; + template class IDatastoreObjectList; + template class IDatastoreObjectList; + template class IDatastoreObjectList; + template class IDatastoreObjectList; + template class IDatastoreObjectList; + + } // ns +} // ns diff --git a/src/blackmisc/db/datastoreobjectlist.h b/src/blackmisc/db/datastoreobjectlist.h new file mode 100644 index 000000000..3d4af417e --- /dev/null +++ b/src/blackmisc/db/datastoreobjectlist.h @@ -0,0 +1,95 @@ +/* Copyright (C) 2015 + * swift project Community / Contributors + * + * This file is part of swift project. It is subject to the license terms in the LICENSE file found in the top-level + * directory of this distribution and at http://www.swift-project.org/license.html. No part of swift project, + * including this file, may be copied, modified, propagated, or distributed except according to the terms + * contained in the LICENSE file. + */ + +//! \file + +#ifndef BLACKMISC_DB_DATABASEOBJECTLIST_H +#define BLACKMISC_DB_DATABASEOBJECTLIST_H + +#include "blackmisc/timestampobjectlist.h" + +#include +#include +#include + +namespace BlackMisc +{ + namespace Db + { + //! List of objects read from database. + //! Such objects should implement \sa ITimestampBased and \sa IDatastoreObjectWithIntegerKey or \sa IDatastoreObjectWithStringKey + template class IDatastoreObjectList : public ITimestampObjectList + { + public: + //! Object with key, notFound otherwise + OBJ findByKey(KEYTYPE key, const OBJ ¬Found = OBJ()) const; + + //! Object with max.key + OBJ maxKeyObject() const; + + //! Sort by timestamp + void sortByKey(); + + //! All keys as list + QList toDbKeyList() const; + + //! Max.key value (making sense with integer key) + KEYTYPE getMaxKey(bool *ok = nullptr) const; + + //! Remove objects with key + int removeObjectsWithKeys(const QList &keys); + + //! Remove objects without key + int removeObjectsWithoutDbKey(); + + //! Update or insert data (based on DB key) + int replaceOrAddObjectsByKey(const CONTAINER &container); + + //! From DB JSON with default prefixes + static CONTAINER fromDatabaseJson(const QJsonArray &array); + + //! From DB JSON + static CONTAINER fromDatabaseJson(const QJsonArray &array, const QString &prefix); + + protected: + //! Constructor + IDatastoreObjectList(); + }; + + //! \cond PRIVATE + class CDbInfo; + class CDbInfoList; + + namespace Aviation + { + class CLivery; + class CLiveryList; + } + + namespace Simulation + { + class CDistributor; + class CDistributorList; + class CAircraftModel; + class CAircraftModelList; + } + + extern template class BLACKMISC_EXPORT_TEMPLATE IDatastoreObjectList; + extern template class BLACKMISC_EXPORT_TEMPLATE IDatastoreObjectList; + extern template class BLACKMISC_EXPORT_TEMPLATE IDatastoreObjectList; + extern template class BLACKMISC_EXPORT_TEMPLATE IDatastoreObjectList; + extern template class BLACKMISC_EXPORT_TEMPLATE IDatastoreObjectList; + extern template class BLACKMISC_EXPORT_TEMPLATE IDatastoreObjectList; + extern template class BLACKMISC_EXPORT_TEMPLATE IDatastoreObjectList; + //! \endcond + + } // ns +} //ns + +#endif //guard diff --git a/src/blackmisc/db/datastoreutility.cpp b/src/blackmisc/db/datastoreutility.cpp new file mode 100644 index 000000000..5adb12e37 --- /dev/null +++ b/src/blackmisc/db/datastoreutility.cpp @@ -0,0 +1,150 @@ +/* Copyright (C) 2015 + * swift project Community / Contributors + * + * This file is part of swift project. It is subject to the license terms in the LICENSE file found in the top-level + * directory of this distribution and at http://www.swift-project.org/license.html. No part of swift project, + * including this file, may be copied, modified, propagated, or distributed except according to the terms + * contained in the LICENSE file. + */ + +#include "blackmisc/db/datastoreutility.h" +#include "blackmisc/logcategory.h" +#include "blackmisc/logcategorylist.h" +#include "blackmisc/simulation/aircraftmodellist.h" +#include "blackmisc/statusmessage.h" +#include "blackmisc/statusmessagelist.h" +#include "blackmisc/stringutils.h" + +#include +#include +#include +#include +#include +#include + +using namespace BlackMisc; +using namespace BlackMisc::Simulation; + +namespace BlackMisc +{ + namespace Db + { + bool CDatastoreUtility::dbBoolStringToBool(const QString &dbBool) + { + return BlackMisc::stringToBool(dbBool); + } + + const QString &CDatastoreUtility::boolToDbYN(bool v) + { + static const QString y("Y"); + static const QString n("N"); + return v ? y : n; + } + + int CDatastoreUtility::extractIntegerKey(const QString &stringWithKey) + { + QString ks(stringWithKey.trimmed()); + if (ks.isEmpty()) { return -1; } + bool ok = false; + int key = ks.toInt(&ok); + if (ok) { return key; } // only a number + + // key in string with () + int i1 = ks.lastIndexOf('('); + if (i1 < 0) { return -1; } + int i2 = ks.lastIndexOf(')'); + if (i2 <= i1 + 1) { return -1;} + QString n(ks.mid(i1 + 1, i2 - i1 - 1)); + ok = false; + key = n.toInt(&ok); + return ok ? key : -1; + } + + QDateTime CDatastoreUtility::parseTimestamp(const QString ×tamp) + { + if (!timestamp.isEmpty()) + { + QString ts(timestamp.trimmed().remove(' ').remove('-').remove(':')); // normalize + QDateTime dt = QDateTime::fromString(ts, "yyyyMMddHHmmss"); + dt.setTimeZone(QTimeZone::utc()); + return dt; + } + else + { + return QDateTime(); + } + } + + bool CDatastoreUtility::parseSwiftPublishResponse(const QString &jsonResponse, CAircraftModelList &publishedModels, CAircraftModelList &skippedModels, CStatusMessageList &messages) + { + static const CLogCategoryList cats({ CLogCategory::swiftDbWebservice()}); + if (jsonResponse.isEmpty()) + { + messages.push_back(CStatusMessage(cats, CStatusMessage::SeverityError, "Empty JSON data")); + return false; + } + + QJsonDocument jsonDoc(QJsonDocument::fromJson(jsonResponse.toUtf8())); + + // array of messages + if (jsonDoc.isArray()) + { + CStatusMessageList msgs(CStatusMessageList::fromDatabaseJson(jsonDoc.array())); + messages.push_back(msgs); + return true; + } + + // no object -> most likely some fucked up HTML string with the PHP error + if (!jsonDoc.isObject()) + { + QString phpError(jsonResponse); + phpError.remove(QRegExp("<[^>]*>")); + messages.push_back(CStatusMessage(cats, CStatusMessage::SeverityError, phpError)); + return false; + } + + // fully blown object + QJsonObject json(jsonDoc.object()); + bool data = false; + if (json.contains("msgs")) + { + QJsonValue msgJson(json.take("msgs")); + CStatusMessageList msgs(CStatusMessageList::fromDatabaseJson(msgJson.toArray())); + if (!msgs.isEmpty()) + { + messages.push_back(msgs); + data = true; + } + } + + if (json.contains("publishedModels")) + { + QJsonValue publishedJson(json.take("publishedModels")); + CAircraftModelList published = CAircraftModelList::fromDatabaseJson(publishedJson.toArray(), ""); + if (!published.isEmpty()) + { + publishedModels.push_back(published); + data = true; + } + } + + if (json.contains("skippedModels")) + { + QJsonValue skippedJson(json.take("skippedModels")); + CAircraftModelList skipped = CAircraftModelList::fromDatabaseJson(skippedJson.toArray(), ""); + if (!skipped.isEmpty()) + { + skippedModels.push_back(skipped); + data = true; + } + } + + if (!data) + { + messages.push_back(CStatusMessage(cats, CStatusMessage::SeverityError, "Received response, but no JSON data")); + } + + return data; + } + } // ns +} // ns diff --git a/src/blackmisc/db/datastoreutility.h b/src/blackmisc/db/datastoreutility.h new file mode 100644 index 000000000..cf69bc5f7 --- /dev/null +++ b/src/blackmisc/db/datastoreutility.h @@ -0,0 +1,54 @@ +/* Copyright (C) 2015 + * swift project Community / Contributors + * + * This file is part of swift project. It is subject to the license terms in the LICENSE file found in the top-level + * directory of this distribution and at http://www.swift-project.org/license.html. No part of swift project, + * including this file, may be copied, modified, propagated, or distributed except according to the terms + * contained in the LICENSE file. + */ + +//! \file + +#ifndef BLACKMISC_DB_DATASTOREUTILITY_H +#define BLACKMISC_DB_DATASTOREUTILITY_H + +#include "blackmisc/blackmiscexport.h" + +#include +#include + +namespace BlackMisc +{ + class CStatusMessageList; + namespace Simulation { class CAircraftModelList; } + + namespace Db + { + /*! + * Class with datastore related utilities + */ + class BLACKMISC_EXPORT CDatastoreUtility + { + public: + //! No constructor + CDatastoreUtility() = delete; + + //! DB Bool value to bool + static bool dbBoolStringToBool(const QString &dbBool); + + //! Bool to DB yes/no + static const QString &boolToDbYN(bool v); + + //! Extract key from string like "MyAircraft (33)" + static int extractIntegerKey(const QString &stringWithKey); + + //! Parse a timestamp object + static QDateTime parseTimestamp(const QString ×tamp); + + //! Get data from a DB response + static bool parseSwiftPublishResponse(const QString &jsonResponse, BlackMisc::Simulation::CAircraftModelList &publishedModels, BlackMisc::Simulation::CAircraftModelList &skippedModels, BlackMisc::CStatusMessageList &messages); + }; + } // namespace +} // namespace + +#endif // guard