Show global directory errors messages

Previously, only error messages for failed
items have been shown.
This commit is contained in:
Martchus 2017-07-14 20:14:15 +02:00
parent c78fec4151
commit c4ccda93c9
7 changed files with 110 additions and 88 deletions

View File

@ -434,9 +434,10 @@ void Application::printDir(const SyncthingDir *dir)
printProperty("Auto-normalize", dir->autoNormalize);
printProperty("Rescan interval", TimeSpan::fromSeconds(dir->rescanInterval));
printProperty("Min. free disk percentage", dir->minDiskFreePercentage);
if (!dir->errors.empty()) {
cout << " Errors\n";
for (const SyncthingDirError &error : dir->errors) {
printProperty("Error", dir->globalError);
if (!dir->itemErrors.empty()) {
cout << " Failed items\n";
for (const SyncthingItemError &error : dir->itemErrors) {
printProperty(" - Message", error.message);
printProperty(" File", error.path);
}

View File

@ -1372,7 +1372,15 @@ void SyncthingConnection::readStatusChangedEvent(DateTime eventTime, const QJson
int index;
if (SyncthingDir *dirInfo = findDirInfo(dir, index)) {
// directory is already known -> just update status
if (dirInfo->assignStatus(eventData.value(QStringLiteral("to")).toString(), eventTime)) {
bool statusChanged = dirInfo->assignStatus(eventData.value(QStringLiteral("to")).toString(), eventTime);
if (dirInfo->status == SyncthingDirStatus::OutOfSync) {
const QString errorMessage(eventData.value(QStringLiteral("error")).toString());
if (!errorMessage.isEmpty()) {
dirInfo->globalError = errorMessage;
statusChanged = true;
}
}
if (statusChanged) {
emit dirStatusChanged(*dirInfo, index);
}
} else {
@ -1443,17 +1451,17 @@ void SyncthingConnection::readDirEvent(DateTime eventTime, const QString &eventT
for (const QJsonValue &errorVal : errors) {
const QJsonObject error(errorVal.toObject());
if (!error.isEmpty()) {
auto &errors = dirInfo->errors;
SyncthingDirError dirError(
auto &errors = dirInfo->itemErrors;
SyncthingItemError dirError(
error.value(QStringLiteral("error")).toString(), error.value(QStringLiteral("path")).toString());
if (find(errors.cbegin(), errors.cend(), dirError) == errors.cend()) {
errors.emplace_back(move(dirError));
dirInfo->assignStatus(SyncthingDirStatus::OutOfSync, eventTime);
// emit newNotification() for new errors
auto &previousErrors = dirInfo->previousErrors;
if (find(previousErrors.cbegin(), previousErrors.cend(), dirInfo->errors.back()) == previousErrors.cend()) {
emitNotification(eventTime, dirInfo->errors.back().message);
const auto &previousErrors = dirInfo->previousItemErrors;
if (find(previousErrors.cbegin(), previousErrors.cend(), dirInfo->itemErrors.back()) == previousErrors.cend()) {
emitNotification(eventTime, dirInfo->itemErrors.back().message);
}
}
}
@ -1586,7 +1594,7 @@ void SyncthingConnection::readItemFinished(DateTime eventTime, const QJsonObject
}
} else if (dirInfo->status == SyncthingDirStatus::OutOfSync) {
// FIXME: find better way to check whether the event is still relevant
dirInfo->errors.emplace_back(error, item);
dirInfo->itemErrors.emplace_back(error, item);
dirInfo->status = SyncthingDirStatus::OutOfSync;
emit dirStatusChanged(*dirInfo, index);
emitNotification(eventTime, error);

View File

@ -31,48 +31,39 @@ QString statusString(SyncthingDirStatus status)
}
}
/*!
* \brief Assigns the status from the specified status string.
* \returns Returns whether the status has actually changed.
*/
bool SyncthingDir::assignStatus(const QString &statusStr, ChronoUtilities::DateTime time)
bool SyncthingDir::checkWhetherStatusUpdateRelevant(DateTime time)
{
// ignore old updates
if (lastStatusUpdate > time) {
return false;
} else {
lastStatusUpdate = time;
}
SyncthingDirStatus newStatus;
if (statusStr == QLatin1String("idle")) {
progressPercentage = 0;
newStatus = SyncthingDirStatus::Idle;
} else if (statusStr == QLatin1String("scanning")) {
newStatus = SyncthingDirStatus::Scanning;
} else if (statusStr == QLatin1String("syncing")) {
// ensure status changed signal is emitted
if (!errors.empty()) {
status = SyncthingDirStatus::Unknown;
}
// errors become obsolete; however errors must be kept as previous errors to be able
// to identify new errors occuring during this sync attempt as known errors
previousErrors.clear();
previousErrors.swap(errors);
newStatus = SyncthingDirStatus::Synchronizing;
} else if (statusStr == QLatin1String("error")) {
progressPercentage = 0;
newStatus = SyncthingDirStatus::OutOfSync;
} else {
newStatus = SyncthingDirStatus::Idle;
}
if (newStatus == SyncthingDirStatus::Idle) {
if (!errors.empty()) {
lastStatusUpdate = time;
return true;
}
bool SyncthingDir::finalizeStatusUpdate(SyncthingDirStatus newStatus)
{
// check whether out-of-sync or unshared
switch (newStatus) {
case SyncthingDirStatus::Unknown:
case SyncthingDirStatus::Idle:
case SyncthingDirStatus::Unshared:
if (!itemErrors.empty()) {
newStatus = SyncthingDirStatus::OutOfSync;
} else if (devices.size() < 2) {
// FIXME: we can assume only own device is assigned, correct?
newStatus = SyncthingDirStatus::Unshared;
}
break;
default:;
}
// clear global error if not out-of-sync anymore
if (newStatus != SyncthingDirStatus::OutOfSync) {
globalError.clear();
}
// actuall update the status ...
if (newStatus != status) {
// ... and also update last scan time
switch (status) {
case SyncthingDirStatus::Scanning:
lastScanTime = DateTime::now();
@ -85,37 +76,39 @@ bool SyncthingDir::assignStatus(const QString &statusStr, ChronoUtilities::DateT
return false;
}
bool SyncthingDir::assignStatus(SyncthingDirStatus newStatus, DateTime time)
/*!
* \brief Assigns the status from the specified status string.
* \returns Returns whether the status has actually changed.
*/
bool SyncthingDir::assignStatus(const QString &statusStr, ChronoUtilities::DateTime time)
{
if (lastStatusUpdate > time) {
if (!checkWhetherStatusUpdateRelevant(time)) {
return false;
}
// identify statusStr
SyncthingDirStatus newStatus;
if (statusStr == QLatin1String("idle")) {
progressPercentage = 0;
newStatus = SyncthingDirStatus::Idle;
} else if (statusStr == QLatin1String("scanning")) {
newStatus = SyncthingDirStatus::Scanning;
} else if (statusStr == QLatin1String("syncing")) {
// ensure status changed signal is emitted
if (!itemErrors.empty()) {
status = SyncthingDirStatus::Unknown;
}
// errors become obsolete; however errors must be kept as previous errors to be able
// to identify new errors occuring during this sync attempt as known errors
previousItemErrors.clear();
previousItemErrors.swap(itemErrors);
newStatus = SyncthingDirStatus::Synchronizing;
} else if (statusStr == QLatin1String("error")) {
progressPercentage = 0;
newStatus = SyncthingDirStatus::OutOfSync;
} else {
lastStatusUpdate = time;
newStatus = SyncthingDirStatus::Idle;
}
switch (newStatus) {
case SyncthingDirStatus::Unknown:
case SyncthingDirStatus::Idle:
case SyncthingDirStatus::Unshared:
if (!errors.empty()) {
newStatus = SyncthingDirStatus::OutOfSync;
} else if (devices.size() < 2) {
// FIXME: we can assume only own device is assigned, correct?
newStatus = SyncthingDirStatus::Unshared;
}
break;
default:;
}
if (newStatus != status) {
switch (status) {
case SyncthingDirStatus::Scanning:
lastScanTime = DateTime::now();
break;
default:;
}
status = newStatus;
return true;
}
return false;
return finalizeStatusUpdate(newStatus);
}
QString SyncthingDir::statusString() const

View File

@ -1,4 +1,4 @@
#ifndef DATA_SYNCTHINGDIR_H
#ifndef DATA_SYNCTHINGDIR_H
#define DATA_SYNCTHINGDIR_H
#include "./global.h"
@ -24,14 +24,14 @@ Q_ENUM_NS(SyncthingDirStatus)
QString statusString(SyncthingDirStatus status);
struct LIB_SYNCTHING_CONNECTOR_EXPORT SyncthingDirError {
SyncthingDirError(const QString &message = QString(), const QString &path = QString())
struct LIB_SYNCTHING_CONNECTOR_EXPORT SyncthingItemError {
SyncthingItemError(const QString &message = QString(), const QString &path = QString())
: message(message)
, path(path)
{
}
bool operator==(const SyncthingDirError &other) const
bool operator==(const SyncthingItemError &other) const
{
return message == other.message && path == other.path;
}
@ -81,8 +81,9 @@ struct LIB_SYNCTHING_CONNECTOR_EXPORT SyncthingDir {
ChronoUtilities::DateTime lastStatusUpdate;
int progressPercentage = 0;
int progressRate = 0;
std::vector<SyncthingDirError> errors;
std::vector<SyncthingDirError> previousErrors;
QString globalError;
std::vector<SyncthingItemError> itemErrors;
std::vector<SyncthingItemError> previousItemErrors;
int globalBytes = 0, globalDeleted = 0, globalFiles = 0;
int localBytes = 0, localDeleted = 0, localFiles = 0;
int neededByted = 0, neededFiles = 0;
@ -95,6 +96,10 @@ struct LIB_SYNCTHING_CONNECTOR_EXPORT SyncthingDir {
int blocksToBeDownloaded = 0;
unsigned int downloadPercentage = 0;
QString downloadLabel;
private:
bool checkWhetherStatusUpdateRelevant(ChronoUtilities::DateTime time);
bool finalizeStatusUpdate(SyncthingDirStatus newStatus);
};
inline SyncthingDir::SyncthingDir(const QString &id, const QString &label, const QString &path)
@ -109,9 +114,14 @@ inline QString SyncthingDir::displayName() const
return label.isEmpty() ? id : label;
}
inline bool SyncthingDir::assignStatus(SyncthingDirStatus newStatus, ChronoUtilities::DateTime time)
{
return checkWhetherStatusUpdateRelevant(time) && finalizeStatusUpdate(newStatus);
}
} // namespace Data
Q_DECLARE_METATYPE(Data::SyncthingDirError)
Q_DECLARE_METATYPE(Data::SyncthingItemError)
Q_DECLARE_METATYPE(Data::SyncthingItemDownloadProgress)
Q_DECLARE_METATYPE(Data::SyncthingDir)

View File

@ -173,14 +173,14 @@ void MiscTests::testSyncthingDir()
CPPUNIT_ASSERT_EQUAL(updateTime, dir.lastStatusUpdate);
CPPUNIT_ASSERT(dir.lastScanTime >= lastScanTime);
dir.errors.emplace_back(QStringLiteral("message"), QStringLiteral("path"));
dir.itemErrors.emplace_back(QStringLiteral("message"), QStringLiteral("path"));
CPPUNIT_ASSERT(dir.assignStatus(SyncthingDirStatus::Idle, updateTime += TimeSpan::fromMinutes(1.5)));
CPPUNIT_ASSERT_EQUAL(QStringLiteral("out of sync"), dir.statusString());
CPPUNIT_ASSERT(!dir.assignStatus(SyncthingDirStatus::Idle, updateTime += TimeSpan::fromMinutes(1.5)));
CPPUNIT_ASSERT(dir.assignStatus(QStringLiteral("syncing"), updateTime += TimeSpan::fromMinutes(1.5)));
dir.errors.clear();
dir.itemErrors.clear();
CPPUNIT_ASSERT(dir.assignStatus(QStringLiteral("error"), updateTime += TimeSpan::fromMinutes(1.5)));
CPPUNIT_ASSERT_EQUAL(QStringLiteral("out of sync"), dir.statusString());
@ -190,11 +190,11 @@ void MiscTests::testSyncthingDir()
CPPUNIT_ASSERT_MESSAGE("older status discarded", !dir.assignStatus(QStringLiteral("scanning"), updateTime - TimeSpan::fromSeconds(1)));
CPPUNIT_ASSERT_EQUAL(QStringLiteral("idle"), dir.statusString());
dir.errors.emplace_back(QStringLiteral("message"), QStringLiteral("path"));
dir.itemErrors.emplace_back(QStringLiteral("message"), QStringLiteral("path"));
CPPUNIT_ASSERT(dir.assignStatus(QStringLiteral("idle"), updateTime += TimeSpan::fromMinutes(1.5)));
CPPUNIT_ASSERT_EQUAL(QStringLiteral("out of sync"), dir.statusString());
dir.errors.clear();
dir.itemErrors.clear();
dir.devices.removeLast();
CPPUNIT_ASSERT(dir.assignStatus(QStringLiteral("idle"), updateTime += TimeSpan::fromMinutes(1.5)));
CPPUNIT_ASSERT_EQUAL_MESSAGE("dir considered unshared when only one dev present", QStringLiteral("unshared"), dir.statusString());

View File

@ -122,9 +122,19 @@ QVariant SyncthingDirectoryModel::data(const QModelIndex &index, int role) const
case 6:
return dir.lastFileName.isEmpty() ? tr("unknown") : dir.lastFileName;
case 7:
return dir.errors.empty()
? tr("none")
: tr("%1 item(s) out of sync", nullptr, static_cast<int>(dir.errors.size())).arg(dir.errors.size());
if (!dir.globalError.isEmpty() || !dir.itemErrors.empty()) {
if (dir.itemErrors.empty()) {
return dir.globalError;
}
if (dir.globalError.isEmpty()) {
return tr("%1 item(s) out of sync", nullptr, static_cast<int>(dir.itemErrors.size())).arg(dir.itemErrors.size());
}
return tr("%1 and %2 item(s) out of sync", nullptr, static_cast<int>(dir.itemErrors.size()))
.arg(dir.globalError)
.arg(dir.itemErrors.size());
} else {
return tr("none");
}
}
break;
}
@ -143,7 +153,7 @@ QVariant SyncthingDirectoryModel::data(const QModelIndex &index, int role) const
return dir.lastFileName.isEmpty() ? Colors::gray(m_brightColors)
: (dir.lastFileDeleted ? Colors::red(m_brightColors) : QVariant());
case 7:
return dir.errors.empty() ? Colors::gray(m_brightColors) : Colors::red(m_brightColors);
return dir.globalError.isEmpty() && dir.itemErrors.empty() ? Colors::gray(m_brightColors) : Colors::red(m_brightColors);
}
}
break;
@ -169,10 +179,10 @@ QVariant SyncthingDirectoryModel::data(const QModelIndex &index, int role) const
}
break;
case 7:
if (!dir.errors.empty()) {
if (!dir.itemErrors.empty()) {
QStringList errors;
errors.reserve(static_cast<int>(dir.errors.size()));
for (const auto &error : dir.errors) {
errors.reserve(static_cast<int>(dir.itemErrors.size()));
for (const auto &error : dir.itemErrors) {
errors << error.path;
}
return QVariant(QStringLiteral("<b>") % tr("Failed items") % QStringLiteral("</b><ul><li>")

View File

@ -48,11 +48,11 @@ void DirView::mouseReleaseEvent(QMouseEvent *event)
emit openDir(*dir);
}
}
} else if (clickedIndex.row() == 7 && !dir->errors.empty()) {
} else if (clickedIndex.row() == 7 && !dir->itemErrors.empty()) {
// show errors
auto *textViewDlg = new TextViewDialog(tr("Errors of %1").arg(dir->label.isEmpty() ? dir->id : dir->label));
auto *browser = textViewDlg->browser();
for (const SyncthingDirError &error : dir->errors) {
for (const SyncthingItemError &error : dir->itemErrors) {
browser->append(error.path % QChar(':') % QChar(' ') % QChar('\n') % error.message % QChar('\n'));
}
textViewDlg->show();