Improve UX of database statistics page

* Fix #3766 - move database statistics processing into async task and only perform the calculation when the statistics tab is activated.
This commit is contained in:
Jonathan White 2019-11-03 09:28:09 -05:00
parent f9d2696046
commit 87ca7c7f7b
3 changed files with 61 additions and 31 deletions

View file

@ -18,6 +18,7 @@
#include "DatabaseSettingsWidgetStatistics.h" #include "DatabaseSettingsWidgetStatistics.h"
#include "ui_DatabaseSettingsWidgetStatistics.h" #include "ui_DatabaseSettingsWidgetStatistics.h"
#include "core/AsyncTask.h"
#include "core/Database.h" #include "core/Database.h"
#include "core/FilePath.h" #include "core/FilePath.h"
#include "core/Group.h" #include "core/Group.h"
@ -123,7 +124,8 @@ namespace
++nPwdsShort; ++nPwdsShort;
} }
if (ZxcvbnMatch(pwd.toLatin1(), nullptr, nullptr) < 65) { // Speed up Zxcvbn process by excluding very long passwords and most passphrases
if (pwd.size() < 25 && ZxcvbnMatch(pwd.toLatin1(), nullptr, nullptr) < 65) {
++nPwdsWeak; ++nPwdsWeak;
} }
@ -142,6 +144,11 @@ DatabaseSettingsWidgetStatistics::DatabaseSettingsWidgetStatistics(QWidget* pare
, m_errIcon(FilePath::instance()->icon("status", "dialog-error")) , m_errIcon(FilePath::instance()->icon("status", "dialog-error"))
{ {
m_ui->setupUi(this); m_ui->setupUi(this);
m_referencesModel.reset(new QStandardItemModel());
m_referencesModel->setHorizontalHeaderLabels(QStringList() << tr("Name") << tr("Value"));
m_ui->statisticsTableView->setModel(m_referencesModel.data());
m_ui->statisticsTableView->horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents);
} }
DatabaseSettingsWidgetStatistics::~DatabaseSettingsWidgetStatistics() DatabaseSettingsWidgetStatistics::~DatabaseSettingsWidgetStatistics()
@ -165,48 +172,63 @@ void DatabaseSettingsWidgetStatistics::addStatsRow(QString name, QString value,
void DatabaseSettingsWidgetStatistics::loadSettings(QSharedPointer<Database> db) void DatabaseSettingsWidgetStatistics::loadSettings(QSharedPointer<Database> db)
{ {
m_referencesModel.reset(new QStandardItemModel()); m_db = std::move(db);
m_referencesModel->setHorizontalHeaderLabels(QStringList() << tr("Name") << tr("Value")); m_statsCalculated = false;
m_referencesModel->clear();
addStatsRow(tr("Please wait, database statistics are being calculated..."), "");
}
const auto stats = Stats(db); void DatabaseSettingsWidgetStatistics::showEvent(QShowEvent* event)
addStatsRow(tr("Database name"), db->metadata()->name()); {
addStatsRow(tr("Description"), db->metadata()->description()); QWidget::showEvent(event);
addStatsRow(tr("Location"), db->filePath());
addStatsRow(tr("Last saved"), stats.modified.toString(Qt::DefaultLocaleShortDate)); if (!m_statsCalculated) {
// Perform stats calculation on next event loop to allow widget to appear
m_statsCalculated = true;
QTimer::singleShot(0, this, SLOT(calculateStats()));
}
}
void DatabaseSettingsWidgetStatistics::calculateStats()
{
const auto stats = AsyncTask::runAndWaitForFuture([this] { return new Stats(m_db); });
m_referencesModel->clear();
addStatsRow(tr("Database name"), m_db->metadata()->name());
addStatsRow(tr("Description"), m_db->metadata()->description());
addStatsRow(tr("Location"), m_db->filePath());
addStatsRow(tr("Last saved"), stats->modified.toString(Qt::DefaultLocaleShortDate));
addStatsRow(tr("Unsaved changes"), addStatsRow(tr("Unsaved changes"),
db->isModified() ? tr("yes") : tr("no"), m_db->isModified() ? tr("yes") : tr("no"),
db->isModified(), m_db->isModified(),
tr("The database was modified, but the changes have not yet been saved to disk.")); tr("The database was modified, but the changes have not yet been saved to disk."));
addStatsRow(tr("Number of groups"), QString::number(stats.nGroups)); addStatsRow(tr("Number of groups"), QString::number(stats->nGroups));
addStatsRow(tr("Number of entries"), QString::number(stats.nEntries)); addStatsRow(tr("Number of entries"), QString::number(stats->nEntries));
addStatsRow(tr("Number of expired entries"), addStatsRow(tr("Number of expired entries"),
QString::number(stats.nExpired), QString::number(stats->nExpired),
stats.isAnyExpired(), stats->isAnyExpired(),
tr("The database contains entries that have expired.")); tr("The database contains entries that have expired."));
addStatsRow(tr("Unique passwords"), QString::number(stats.nPwdsUnique)); addStatsRow(tr("Unique passwords"), QString::number(stats->nPwdsUnique));
addStatsRow(tr("Non-unique passwords"), addStatsRow(tr("Non-unique passwords"),
QString::number(stats.nPwdsReused), QString::number(stats->nPwdsReused),
stats.areTooManyPwdsReused(), stats->areTooManyPwdsReused(),
tr("More than 10% of passwords are reused. Use unique passwords when possible.")); tr("More than 10% of passwords are reused. Use unique passwords when possible."));
addStatsRow(tr("Maximum password reuse"), addStatsRow(tr("Maximum password reuse"),
QString::number(stats.maxPwdReuse()), QString::number(stats->maxPwdReuse()),
stats.arePwdsReusedTooOften(), stats->arePwdsReusedTooOften(),
tr("Some passwords are used more than three times. Use unique passwords when possible.")); tr("Some passwords are used more than three times. Use unique passwords when possible."));
addStatsRow(tr("Number of short passwords"), addStatsRow(tr("Number of short passwords"),
QString::number(stats.nPwdsShort), QString::number(stats->nPwdsShort),
stats.nPwdsShort > 0, stats->nPwdsShort > 0,
tr("Recommended minimum password length is at least 8 characters.")); tr("Recommended minimum password length is at least 8 characters."));
addStatsRow(tr("Number of weak passwords"), addStatsRow(tr("Number of weak passwords"),
QString::number(stats.nPwdsWeak), QString::number(stats->nPwdsWeak),
stats.nPwdsWeak > 0, stats->nPwdsWeak > 0,
tr("Recommend using long, randomized passwords with a rating of 'good' or 'excellent'.")); tr("Recommend using long, randomized passwords with a rating of 'good' or 'excellent'."));
addStatsRow(tr("Average password length"), addStatsRow(tr("Average password length"),
tr("%1 characters").arg(stats.averagePwdLength()), tr("%1 characters").arg(stats->averagePwdLength()),
stats.isAvgPwdTooShort(), stats->isAvgPwdTooShort(),
tr("Average password length is less than ten characters. Longer passwords provide more security.")); tr("Average password length is less than ten characters. Longer passwords provide more security."));
m_ui->sharedGroupsView->setModel(m_referencesModel.data());
m_ui->sharedGroupsView->horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents);
} }
void DatabaseSettingsWidgetStatistics::saveSettings() void DatabaseSettingsWidgetStatistics::saveSettings()

View file

@ -39,11 +39,19 @@ public:
void loadSettings(QSharedPointer<Database> db); void loadSettings(QSharedPointer<Database> db);
void saveSettings(); void saveSettings();
protected:
void showEvent(QShowEvent* event) override;
private slots:
void calculateStats();
private: private:
QScopedPointer<Ui::DatabaseSettingsWidgetStatistics> m_ui; QScopedPointer<Ui::DatabaseSettingsWidgetStatistics> m_ui;
bool m_statsCalculated = false;
QIcon m_errIcon; QIcon m_errIcon;
QScopedPointer<QStandardItemModel> m_referencesModel; QScopedPointer<QStandardItemModel> m_referencesModel;
QSharedPointer<Database> m_db;
void addStatsRow(QString name, QString value, bool bad = false, QString badMsg = ""); void addStatsRow(QString name, QString value, bool bad = false, QString badMsg = "");
}; };

View file

@ -24,13 +24,13 @@
<number>0</number> <number>0</number>
</property> </property>
<item> <item>
<widget class="QGroupBox" name="enableGroupBox"> <widget class="QGroupBox" name="statisticsGroupBox">
<property name="title"> <property name="title">
<string>Statistics</string> <string>Statistics</string>
</property> </property>
<layout class="QVBoxLayout" name="verticalLayout_2"> <layout class="QVBoxLayout" name="verticalLayout_2">
<item> <item>
<widget class="QTableView" name="sharedGroupsView"> <widget class="QTableView" name="statisticsTableView">
<property name="editTriggers"> <property name="editTriggers">
<set>QAbstractItemView::NoEditTriggers</set> <set>QAbstractItemView::NoEditTriggers</set>
</property> </property>
@ -58,7 +58,7 @@
</widget> </widget>
</item> </item>
<item> <item>
<widget class="QLabel" name="label"> <widget class="QLabel" name="tipLabel">
<property name="font"> <property name="font">
<font> <font>
<italic>true</italic> <italic>true</italic>