From 54802e9ea9a97ba9a01bf9c839af643ae4e72e1f Mon Sep 17 00:00:00 2001 From: Klaus Basan Date: Sun, 26 Aug 2018 19:36:17 +0200 Subject: [PATCH] Ref T310, added model statistics dialog * UI for statistics * renamed to "coverageSummaryForModel" * model list "htmlStatistics" --- src/blackcore/aircraftmatcher.cpp | 2 +- src/blackgui/blackgui.pro | 1 + .../views/aircraftmodelstatisticsdialog.cpp | 35 ++++++++ .../views/aircraftmodelstatisticsdialog.h | 45 +++++++++++ .../views/aircraftmodelstatisticsdialog.ui | 80 +++++++++++++++++++ src/blackgui/views/aircraftmodelview.cpp | 36 ++++++--- src/blackgui/views/aircraftmodelview.h | 6 ++ src/blackmisc/db/datastoreobjectlist.cpp | 38 ++++++++- src/blackmisc/db/datastoreobjectlist.h | 9 +++ .../simulation/aircraftmodellist.cpp | 49 ++++++++---- src/blackmisc/simulation/aircraftmodellist.h | 5 +- 11 files changed, 278 insertions(+), 28 deletions(-) create mode 100644 src/blackgui/views/aircraftmodelstatisticsdialog.cpp create mode 100644 src/blackgui/views/aircraftmodelstatisticsdialog.h create mode 100644 src/blackgui/views/aircraftmodelstatisticsdialog.ui diff --git a/src/blackcore/aircraftmatcher.cpp b/src/blackcore/aircraftmatcher.cpp index cd0f4a7d8..400cf6935 100644 --- a/src/blackcore/aircraftmatcher.cpp +++ b/src/blackcore/aircraftmatcher.cpp @@ -120,7 +120,7 @@ namespace BlackCore if (log) { log->clear(); } CMatchingUtils::addLogDetailsToList(log, remoteAircraft, m1.arg(startTime.toString(format))); CMatchingUtils::addLogDetailsToList(log, remoteAircraft, m2.arg(remoteAircraft.getCallsignAsString(), remoteAircraft.getModel().toQString())); - CMatchingUtils::addLogDetailsToList(log, remoteAircraft, m3.arg(modelSet.size()).arg(modelSet.extCoverageSummary(remoteAircraft.getModel()))); + CMatchingUtils::addLogDetailsToList(log, remoteAircraft, m3.arg(modelSet.size()).arg(modelSet.coverageSummaryForModel(remoteAircraft.getModel()))); CMatchingUtils::addLogDetailsToList(log, remoteAircraft, m4.arg(setup.toQString(true))); // Before I really search I check some special conditions diff --git a/src/blackgui/blackgui.pro b/src/blackgui/blackgui.pro index 5ea84a6fd..b303e6015 100644 --- a/src/blackgui/blackgui.pro +++ b/src/blackgui/blackgui.pro @@ -26,6 +26,7 @@ SOURCES += $$PWD/menus/*.cpp HEADERS += $$PWD/views/*.h SOURCES += $$PWD/views/*.cpp +FORMS += $$PWD/views/*.ui HEADERS += $$PWD/components/*.h HEADERS += $$PWD/settings/*.h diff --git a/src/blackgui/views/aircraftmodelstatisticsdialog.cpp b/src/blackgui/views/aircraftmodelstatisticsdialog.cpp new file mode 100644 index 000000000..ff9005e26 --- /dev/null +++ b/src/blackgui/views/aircraftmodelstatisticsdialog.cpp @@ -0,0 +1,35 @@ +/* Copyright (C) 2018 + * 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 "aircraftmodelstatisticsdialog.h" +#include "ui_aircraftmodelstatisticsdialog.h" + +using namespace BlackMisc::Simulation; + +namespace BlackGui +{ + namespace Views + { + CAircraftModelStatisticsDialog::CAircraftModelStatisticsDialog(QWidget *parent) : + QDialog(parent), + ui(new Ui::CAircraftModelStatisticsDialog) + { + ui->setupUi(this); + this->setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); + } + + CAircraftModelStatisticsDialog::~CAircraftModelStatisticsDialog() + { } + + void CAircraftModelStatisticsDialog::analyzeModels(const CAircraftModelList &models) + { + ui->te_Statistics->setHtml(models.htmlStatistics()); + } + } // ns +} // ns diff --git a/src/blackgui/views/aircraftmodelstatisticsdialog.h b/src/blackgui/views/aircraftmodelstatisticsdialog.h new file mode 100644 index 000000000..38fade5e1 --- /dev/null +++ b/src/blackgui/views/aircraftmodelstatisticsdialog.h @@ -0,0 +1,45 @@ +/* Copyright (C) 2018 + * 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 BLACKGUI_VIEW_AIRCRAFTMODELSTATISTICSDIALOG_H +#define BLACKGUI_VIEW_AIRCRAFTMODELSTATISTICSDIALOG_H + +#include "blackmisc/simulation/aircraftmodellist.h" +#include +#include + +namespace Ui { class CAircraftModelStatisticsDialog; } +namespace BlackGui +{ + namespace Views + { + //! Info about the models + class CAircraftModelStatisticsDialog : public QDialog + { + Q_OBJECT + + public: + //! Constructor + explicit CAircraftModelStatisticsDialog(QWidget *parent = nullptr); + + //! Destructor + virtual ~CAircraftModelStatisticsDialog(); + + //! Set and analyze the models + void analyzeModels(const BlackMisc::Simulation::CAircraftModelList &models); + + private: + QScopedPointer ui; + }; + } // ns +} // ns + +#endif // guard diff --git a/src/blackgui/views/aircraftmodelstatisticsdialog.ui b/src/blackgui/views/aircraftmodelstatisticsdialog.ui new file mode 100644 index 000000000..a3b9966ab --- /dev/null +++ b/src/blackgui/views/aircraftmodelstatisticsdialog.ui @@ -0,0 +1,80 @@ + + + CAircraftModelStatisticsDialog + + + + 0 + 0 + 400 + 300 + + + + + 300 + 200 + + + + Aircraft model statistics + + + + + + Statistics + + + true + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Close + + + + + + + + + bb_AircraftModelStatistics + accepted() + CAircraftModelStatisticsDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + bb_AircraftModelStatistics + rejected() + CAircraftModelStatisticsDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/src/blackgui/views/aircraftmodelview.cpp b/src/blackgui/views/aircraftmodelview.cpp index 740baea8b..8b051693d 100644 --- a/src/blackgui/views/aircraftmodelview.cpp +++ b/src/blackgui/views/aircraftmodelview.cpp @@ -7,9 +7,10 @@ * contained in the LICENSE file. */ +#include "aircraftmodelview.h" +#include "viewbase.h" +#include "aircraftmodelstatisticsdialog.h" #include "blackgui/filters/aircraftmodelfilterdialog.h" -#include "blackgui/views/aircraftmodelview.h" -#include "blackgui/views/viewbase.h" #include "blackgui/menus/menuaction.h" #include "blackgui/guiapplication.h" #include "blackgui/guiutility.h" @@ -206,7 +207,7 @@ namespace BlackGui { if (valueVariant.canConvert()) { - CAircraftModel model = valueVariant.value(); + const CAircraftModel model = valueVariant.value(); if (!model.hasModelString()) { return; } const CAircraftModelList models({model}); this->derivedModel()->replaceOrAddByModelString(models); @@ -214,7 +215,7 @@ namespace BlackGui } else if (valueVariant.canConvert()) { - CAircraftModelList models(valueVariant.value()); + const CAircraftModelList models(valueVariant.value()); if (models.isEmpty()) { return; } this->derivedModel()->replaceOrAddByModelString(models); return; @@ -266,7 +267,7 @@ namespace BlackGui } else if (valueVariant.canConvert()) { - CAirlineIcaoCode airline = valueVariant.value(); + const CAirlineIcaoCode airline = valueVariant.value(); if (airline.validate().hasErrorMessages()) { return; } emit requestHandlingOfStashDrop(airline); // I need to convert to stanard livery, which I can`t do here } @@ -288,7 +289,11 @@ namespace BlackGui void CAircraftModelView::customMenu(CMenuActions &menuActions) { - bool used = false; + // Statistics + menuActions.addAction(CIcons::appAircraft16(), "Model statistics", CMenuAction::pathModel(), { this, &CAircraftModelView::displayModelStatisticsDialog }); + + // Stash menus + bool addStashMenu = false; if (m_menus.testFlag(MenuCanStashModels)) { if (!m_menuFlagActions.contains(MenuCanStashModels)) @@ -308,7 +313,7 @@ namespace BlackGui a->setEnabled(canStash); a = menuActions.addActions(initMenuActions(MenuCanStashModels)).last(); a->setChecked(m_stashingClearsSelection); - used = true; + addStashMenu = true; } if (m_menus.testFlag(MenuHighlightStashed)) { @@ -322,9 +327,11 @@ namespace BlackGui } QAction *a = menuActions.addActions(initMenuActions(CViewBaseNonTemplate::MenuHighlightStashed)).first(); a->setChecked(this->derivedModel()->highlightModelStrings()); - used = true; + addStashMenu = true; } - if (used) { menuActions.addMenuStash();} + if (addStashMenu) { menuActions.addMenuStash(); } + + // base class menus COrderableViewWithDbObjects::customMenu(menuActions); } @@ -392,5 +399,16 @@ namespace BlackGui } sGui->displayInStatusBar(CStatusMessage(CStatusMessage::SeverityInfo, "Stashed " + models.getModelStringList(true).join(" "))); } + + void CAircraftModelView::displayModelStatisticsDialog() + { + if (!m_statisticsDialog) + { + m_statisticsDialog = new CAircraftModelStatisticsDialog(this); + } + + m_statisticsDialog->analyzeModels(this->container()); + m_statisticsDialog->exec(); + } } // namespace } // namespace diff --git a/src/blackgui/views/aircraftmodelview.h b/src/blackgui/views/aircraftmodelview.h index 3f347ffb5..536a7870d 100644 --- a/src/blackgui/views/aircraftmodelview.h +++ b/src/blackgui/views/aircraftmodelview.h @@ -46,6 +46,8 @@ namespace BlackGui namespace Menus { class CMenuActions; } namespace Views { + class CAircraftModelStatisticsDialog; + //! Aircraft view class BLACKGUI_EXPORT CAircraftModelView : public COrderableViewWithDbObjects @@ -146,7 +148,11 @@ namespace BlackGui //! Stash shortcut pressed void requestedStash(); + //! Dialog about model statistics + void displayModelStatisticsDialog(); + bool m_stashingClearsSelection = true; //!< stashing unselects + CAircraftModelStatisticsDialog *m_statisticsDialog = nullptr; BlackMisc::Simulation::CSimulatorInfo m_loadingRequiresSimulator; //!< simulator required when loading }; } // ns diff --git a/src/blackmisc/db/datastoreobjectlist.cpp b/src/blackmisc/db/datastoreobjectlist.cpp index d00d8b025..7e47cc21e 100644 --- a/src/blackmisc/db/datastoreobjectlist.cpp +++ b/src/blackmisc/db/datastoreobjectlist.cpp @@ -47,6 +47,30 @@ namespace BlackMisc return this->container().findFirstByOrDefault(&OBJ::getDbKey, key, notFound); } + template + CONTAINER IDatastoreObjectList::findObjectsWithDbKey() const + { + CONTAINER objects; + for (const OBJ &obj : ITimestampObjectList::container()) + { + if (!obj.hasValidDbKey()) { continue; } + objects.push_back(obj); + } + return objects; + } + + template + CONTAINER IDatastoreObjectList::findObjectsWithoutDbKey() const + { + CONTAINER objects; + for (const OBJ &obj : ITimestampObjectList::container()) + { + if (obj.hasValidDbKey()) { continue; } + objects.push_back(obj); + } + return objects; + } + template OBJ IDatastoreObjectList::maxKeyObject() const { @@ -139,8 +163,8 @@ namespace BlackMisc if (keys.contains(obj.getDbKey())) { continue; } newValues.push_back(obj); } - int delta = this->container().size() - newValues.size(); - this->container() = newValues; + const int delta = this->container().size() - newValues.size(); + if (delta > 0) { this->container() = newValues; } return delta; } @@ -197,6 +221,16 @@ namespace BlackMisc return count; } + template + bool IDatastoreObjectList::containsAnyObjectWithoutKey() const + { + for (const OBJ &obj : ITimestampObjectList::container()) + { + if (!obj.hasValidDbKey()) { return true; } + } + return false; + } + template CONTAINER IDatastoreObjectList::fromMultipleJsonFormats(const QJsonObject &jsonObject) { diff --git a/src/blackmisc/db/datastoreobjectlist.h b/src/blackmisc/db/datastoreobjectlist.h index 9a604b343..d6d7f5fd8 100644 --- a/src/blackmisc/db/datastoreobjectlist.h +++ b/src/blackmisc/db/datastoreobjectlist.h @@ -30,6 +30,12 @@ namespace BlackMisc //! Object with key, notFound otherwise OBJ findByKey(KEYTYPE key, const OBJ ¬Found = OBJ()) const; + //! Objects with DB key + CONTAINER findObjectsWithDbKey() const; + + //! Objects without DB key + CONTAINER findObjectsWithoutDbKey() const; + //! Object with max.key OBJ maxKeyObject() const; @@ -66,6 +72,9 @@ namespace BlackMisc //! Number of entries with valid DB key int countWithValidDbKey() const; + //! Any object without key? + bool containsAnyObjectWithoutKey() const; + //! From multiple JSON formats //! \remark supports native swift C++ format, DB format, and cache format static CONTAINER fromMultipleJsonFormats(const QJsonObject &jsonObject); diff --git a/src/blackmisc/simulation/aircraftmodellist.cpp b/src/blackmisc/simulation/aircraftmodellist.cpp index 6f091651f..379722e05 100644 --- a/src/blackmisc/simulation/aircraftmodellist.cpp +++ b/src/blackmisc/simulation/aircraftmodellist.cpp @@ -13,6 +13,7 @@ #include "blackmisc/aviation/airlineicaocode.h" #include "blackmisc/aviation/callsign.h" #include "blackmisc/aviation/livery.h" +#include "blackmisc/math/mathutils.h" #include "blackmisc/compare.h" #include "blackmisc/iterator.h" #include "blackmisc/range.h" @@ -26,6 +27,7 @@ #include using namespace BlackMisc::Network; +using namespace BlackMisc::Math; using namespace BlackMisc::Aviation; using namespace BlackMisc::PhysicalQuantities; @@ -504,20 +506,18 @@ namespace BlackMisc int CAircraftModelList::removeIfNotMatchingSimulator(const CSimulatorInfo &needToMatch) { if (this->isEmpty()) { return 0; } - int c = 0; - for (auto it = this->begin(); it != this->end();) + const int oldSize = this->size(); + CAircraftModelList models; + for (const CAircraftModel &model : *this) { - if (it->matchesSimulator(needToMatch)) + if (model.matchesSimulator(needToMatch)) { - ++it; - } - else - { - c++; - it = this->erase(it); + models.push_back(model); } } - return c; + const int diff = models.size() - oldSize; + if (diff > 0) { *this = models; } + return diff; } int CAircraftModelList::removeAllWithoutModelString() @@ -848,7 +848,7 @@ namespace BlackMisc // normally prefer colors if there is no airline CMatchingUtils::addLogDetailsToList(log, remoteModel.getCallsign(), QString("Prefer color liveries: '%1', airline: '%2', ignore zero scores: '%3'").arg(boolToYesNo(preferColorLiveries), remoteModel.getAirlineIcaoCodeDesignator(), boolToYesNo(ignoreZeroScores))); CMatchingUtils::addLogDetailsToList(log, remoteModel.getCallsign(), QString("--- Start scoring in list with %1 models").arg(this->size())); - CMatchingUtils::addLogDetailsToList(log, remoteModel.getCallsign(), this->extCoverageSummary(remoteModel)); + CMatchingUtils::addLogDetailsToList(log, remoteModel.getCallsign(), this->coverageSummaryForModel(remoteModel)); int c = 1; for (const CAircraftModel &model : *this) @@ -1054,17 +1054,23 @@ namespace BlackMisc QString html; for (const CAircraftModel &model : *this) { - if (!html.isEmpty()) { html += "
"; } - html += model.asHtmlSummary(" "); + html += html.isEmpty() ? + model.asHtmlSummary(" ") : + QStringLiteral("
") % model.asHtmlSummary(" "); } return html; } QString CAircraftModelList::coverageSummary(const QString &separator) const { + if (this->isEmpty()) { return "no models"; } // avoid division by 0 + + const int dbEntries = this->countWithValidDbKey(); + const double dbRatio = CMathUtils::round(static_cast(100 * dbEntries) / this->size(), 1); return QStringLiteral("Entries: ") % QString::number(this->size()) % - QStringLiteral(" | valid DB keys: ") % QString::number(this->countWithValidDbKey()) % separator % + QStringLiteral(" | valid DB keys: ") % QString::number(dbEntries) % + QStringLiteral(" (") % QString::number(dbRatio) % QStringLiteral("%)") % separator % QStringLiteral("color liveries: ") % QString::number(this->countModelsWithColorLivery()) % QStringLiteral(" | airline liveries: ") % QString::number(this->countModelsWithAirlineLivery()) % separator % QStringLiteral("VTOL: ") % QString::number(this->countVtolAircraft()) % @@ -1075,7 +1081,7 @@ namespace BlackMisc QStringLiteral("Simulators: ") % this->countPerSimulator().toQString(); } - QString CAircraftModelList::extCoverageSummary(const CAircraftModel &checkModel, const QString &separator) const + QString CAircraftModelList::coverageSummaryForModel(const CAircraftModel &checkModel, const QString &separator) const { const bool combinedCodeForModel = this->containsCombinedType(checkModel.getAircraftIcaoCode().getCombinedType()); const bool airlineForModel = checkModel.hasAirlineDesignator() && this->containsAirlineLivery(checkModel.getAirlineIcaoCode()); @@ -1087,5 +1093,18 @@ namespace BlackMisc QStringLiteral("") ); } + + QString CAircraftModelList::htmlStatistics() const + { + const bool notOnlyDb = this->containsAnyObjectWithoutKey(); + QString stats = this->coverageSummary("
"); + if (notOnlyDb) + { + const CAircraftModelList dbModels = this->findObjectsWithDbKey(); + stats += QStringLiteral("

DB objects:
---------
") % + dbModels.coverageSummary("
"); + } + return stats; + } } // namespace } // namespace diff --git a/src/blackmisc/simulation/aircraftmodellist.h b/src/blackmisc/simulation/aircraftmodellist.h index 370b92c95..6b0566833 100644 --- a/src/blackmisc/simulation/aircraftmodellist.h +++ b/src/blackmisc/simulation/aircraftmodellist.h @@ -352,7 +352,10 @@ namespace BlackMisc //! What kind of models are represented here? //! \remark checking for some criteria in the given model - QString extCoverageSummary(const CAircraftModel &checkModel, const QString &separator = "\n") const; + QString coverageSummaryForModel(const CAircraftModel &checkModel, const QString &separator = "\n") const; + + //! A HTML summary of the data in the list + QString htmlStatistics() const; }; //! Model per callsign