From b62368501e5b37aea3d8d5feac884a254c71380d Mon Sep 17 00:00:00 2001 From: Arno Date: Sat, 6 Nov 2010 13:44:10 +0100 Subject: Finish ConsistencyChecker This commit reverts the previous one. You don't need a QTimer to start a QThread member function in a separate thread. Calling start() creates a new thread. Implemented filesystem check and polished the dialog. --- consistencycheck.cpp | 192 ++++++++++++++++++++++++++++++++++++++++++--------- consistencycheck.h | 26 +++++-- 2 files changed, 182 insertions(+), 36 deletions(-) diff --git a/consistencycheck.cpp b/consistencycheck.cpp index 7e02304..1d5114c 100644 --- a/consistencycheck.cpp +++ b/consistencycheck.cpp @@ -20,24 +20,34 @@ #include #include #include -#include +#include +#include +#include +#include #include "consistencycheck.h" #include "helper.h" ConsistencyCheck::ConsistencyCheck(QWidget *parent, Qt::WindowFlags f) : QDialog(parent, f), mChecker(0){ // setup widget - mCheckLabel = new QLabel(tr("Checking database consistency")); - mDisplay = new QPlainTextEdit; - mDisplay->setReadOnly(true); + QLabel *okLabel = new QLabel(tr("Ok")); + mOkDisplay = new QPlainTextEdit; + mOkDisplay->setReadOnly(true); + mOkDisplay->setLineWrapMode(QPlainTextEdit::NoWrap); + QLabel *errorLabel = new QLabel(tr("Errors")); + mErrorDisplay = new QPlainTextEdit; + mErrorDisplay->setReadOnly(true); + mErrorDisplay->setLineWrapMode(QPlainTextEdit::NoWrap); mCancelExit = new QPushButton(tr("Close")); mCheckDb = new QPushButton(tr("Check database")); mCheckFs = new QPushButton(tr("Check Filesystem")); - mErrorsOnly = new QCheckBox(tr("Show only errors")); - connect(mErrorsOnly, SIGNAL(stateChanged(int)), this, SLOT(showErrorsChanged(int))); + mCleanup = new QPushButton(tr("Cleanup...")); + connect(mCleanup, SIGNAL(clicked()), this, SLOT(cleanup())); + mCleanup->setEnabled(false); QHBoxLayout *buttonLayout = new QHBoxLayout; buttonLayout->addWidget(mCheckDb); buttonLayout->addWidget(mCheckFs); + buttonLayout->addWidget(mCleanup); buttonLayout->addStretch(); buttonLayout->addWidget(mCancelExit); connect(mCancelExit, SIGNAL(clicked()), this, SLOT(accept())); @@ -54,41 +64,48 @@ ConsistencyCheck::ConsistencyCheck(QWidget *parent, Qt::WindowFlags f) : QDialog mChecker = new ConsistencyChecker(this); connect(mChecker, SIGNAL(started()), this, SLOT(checkerStarted())); connect(mChecker, SIGNAL(finished()), this, SLOT(checkerFinished())); + connect(mChecker, SIGNAL(consistencyMsg(QString)), this, SLOT(addMessage(QString))); // main layout QVBoxLayout *mainLayout = new QVBoxLayout; - mainLayout->addWidget(mCheckLabel); - mainLayout->addWidget(mDisplay); - mainLayout->addWidget(mErrorsOnly); + mainLayout->addWidget(okLabel); + mainLayout->addWidget(mOkDisplay); + mainLayout->addWidget(errorLabel); + mainLayout->addWidget(mErrorDisplay); mainLayout->addLayout(buttonLayout); setWindowTitle(tr("Checking consistency")); setLayout(mainLayout); + setMinimumWidth(600); } void ConsistencyCheck::startChecker(int checkerType){ if(mChecker->status() == ConsistencyChecker::Fail){ return; } + mOkDisplay->clear(); + mErrorDisplay->clear(); switch(checkerType){ case DbCheck: mChecker->setMode(ConsistencyCheck::DbCheck); break; case FsCheck: + mChecker->setMode(ConsistencyCheck::FsCheck); ; break; default: ; } - connect(mChecker, SIGNAL(consistencyMsg(QString)), this, SLOT(addMessage(QString))); mChecker->check(); } void ConsistencyCheck::addMessage(const QString &message){ - QTextCursor cursor(mDisplay->textCursor()); + QTextCursor cursor; QTextCharFormat fmt; if(message.startsWith("OK")){ + cursor = QTextCursor(mOkDisplay->textCursor()); fmt.setForeground(QBrush(Qt::green)); }else{ + cursor = QTextCursor(mErrorDisplay->textCursor()); fmt.setForeground(QBrush(Qt::red)); } QTextBlock curBlock = cursor.block(); @@ -114,24 +131,53 @@ void ConsistencyCheck::checkerFinished(){ mCancelExit->disconnect(); connect(mCancelExit, SIGNAL(clicked()), this, SLOT(accept())); mChecker->setCancel(false); + if(!mChecker->strayFiles().isEmpty() || !mChecker->strayIds().isEmpty()){ + mCleanup->setEnabled(true); + } } void ConsistencyCheck::cancelChecker(){ mChecker->setCancel(true); } -void ConsistencyCheck::showErrorsChanged(int state){ - QTextDocument *doc = mDisplay->document(); - for(QTextBlock it = doc->begin(); it != doc->end(); it = it.next()){ - if(it.text().startsWith("OK")){ - if(state == Qt::Checked){ - it.setVisible(false); - }else{ - it.setVisible(true); +void ConsistencyCheck::cleanup(){ + QStringList strayFiles = mChecker->strayFiles(); + moveFiles(strayFiles); + if(mChecker->mode() == DbCheck){ + QList fileIds = mChecker->strayIds(); + if(!fileIds.isEmpty()){ + QString msg = QString(tr("Really delete %1 entries from database?")).arg(QString::number(fileIds.count())); + int retval = QMessageBox::critical(this, tr("Delete from database"), msg, QMessageBox::Yes | QMessageBox::No); + if(retval == QMessageBox::Yes){ + mChecker->deleteIds(fileIds); + } + } + } + mCleanup->setEnabled(false); +} + +void ConsistencyCheck::moveFiles(const QStringList &files){ + if(files.isEmpty()){ + return; + } + QSettings s; + QString startDir = s.value("paths/selecteddir").toString(); + QString targetDir = QFileDialog::getExistingDirectory(this, tr("Move stray files to..."), startDir); + if(!targetDir.isEmpty()){ + foreach(QString file, files){ + QFileInfo fi(file); + QString tgt = QString("%1%2%3").arg(targetDir).arg(QDir::separator()).arg(fi.fileName()); + QFileInfo tfi(tgt); + if(tfi.exists()){ + QString msg = QString(tr("Target %1 exists. Overwrite?")).arg(tfi.absoluteFilePath()); + int retval = QMessageBox::critical(this, tr("File exists"), msg, QMessageBox::Yes | QMessageBox::No); + if(retval == QMessageBox::No){ + continue; + } } + QFile::rename(file, tgt); } } - mDisplay->viewport()->update(); } ConsistencyChecker::ConsistencyChecker(QObject *parent) : QThread(parent), mCanceled(false), mMode(-1), mStatus(Ok){ @@ -139,6 +185,13 @@ ConsistencyChecker::ConsistencyChecker(QObject *parent) : QThread(parent), mCanc if(!mDb.open()){ mStatus = Fail; } + mFileQuery = new QSqlQuery(mDb); + mFileQuery->prepare("SELECT COUNT(*) FROM files where cmd5sum = :md5"); +} + +ConsistencyChecker::~ConsistencyChecker(){ + delete mFileQuery; + mDb.close(); } void ConsistencyChecker::check(){ @@ -147,41 +200,72 @@ void ConsistencyChecker::check(){ } } +const QStringList ConsistencyChecker::strayFiles(){ + QMutexLocker locker(&mStrayFilesMutex); + return mStrayFiles; +} + +void ConsistencyChecker::clearStrayFiles(){ + QMutexLocker locker(&mStrayFilesMutex); + mStrayFiles.clear(); +} + +const QList ConsistencyChecker::strayIds(){ + QMutexLocker locker(&mStrayIdsMutex); + return mStrayIds; +} + +void ConsistencyChecker::clearStrayIds(){ + QMutexLocker locker(&mStrayIdsMutex); + mStrayIds.clear(); +} + void ConsistencyChecker::setCancel(bool cancel){ QMutexLocker locker(&mCancelMutex); mCanceled = cancel; } -ConsistencyChecker::~ConsistencyChecker(){ - mDb.close(); +void ConsistencyChecker::deleteIds(const QList &ids){ + QSqlQuery idQuery(mDb); + idQuery.prepare("DELETE FROM files WHERE ifiles_id = :id"); + foreach(int id, ids){ + idQuery.bindValue(":id", id); + idQuery.exec(); + } } void ConsistencyChecker::run(){ - switch(mMode){ - case ConsistencyCheck::DbCheck: - QTimer::singleShot(0, this, SLOT(dbCheck())); - exec(); - break; - default: - ; + clearStrayFiles(); + clearStrayIds(); + if(mMode == ConsistencyCheck::FsCheck){ + fsCheck(); + } + if(mMode == ConsistencyCheck::DbCheck){ + dbCheck(); } } void ConsistencyChecker::dbCheck(){ - QSqlQuery fileDbQuery = QSqlQuery("SELECT tfilename, cmd5sum, idvd FROM files ORDER BY tfilename", mDb); + QSqlQuery fileDbQuery = QSqlQuery("SELECT tfilename, cmd5sum, idvd, ifiles_id FROM files ORDER BY tfilename", mDb); while(fileDbQuery.next()){ QMutexLocker locker(&mCancelMutex); if(mCanceled){ - return; + quit(); + }else{ + locker.unlock(); } QString fileName = fileDbQuery.value(0).toString(); QString md5sum = fileDbQuery.value(1).toString(); int dvdNo = fileDbQuery.value(2).toInt(); + int fileId = fileDbQuery.value(3).toInt(); QString fullPath = archivePath(fileName, md5sum); if(fullPath.isEmpty()){ if(dvdNo != -1){ emit consistencyMsg(QString(tr("OK: %1")).arg(fileName)); }else{ + mStrayIdsMutex.lock(); + mStrayIds << fileId; + mStrayIdsMutex.unlock(); emit consistencyMsg(QString(tr("NOT FOUND: %1")).arg(fileName)); } }else{ @@ -192,6 +276,9 @@ void ConsistencyChecker::dbCheck(){ if(mimeType.startsWith("image")){ emit consistencyMsg(QString(tr("OK: %1")).arg(fileName)); }else{ + mStrayFilesMutex.lock(); + mStrayFiles << fullPath; + mStrayFilesMutex.unlock(); QString msg = QString(tr("FOUND: %1 (DVD: %2)")).arg(fullPath).arg(dvdNo); emit consistencyMsg(msg); } @@ -201,6 +288,49 @@ void ConsistencyChecker::dbCheck(){ quit(); } +void ConsistencyChecker::fsCheck(){ + mStrayFilesMutex.lock(); + mStrayFiles.clear(); + mStrayFilesMutex.unlock(); + QSettings s; + QString startDir = s.value("paths/archivedir").toString(); + QFileInfo startFi(startDir); + doFsCheck(startFi); + quit(); +} + +void ConsistencyChecker::doFsCheck(const QFileInfo &start){ + if(start.isDir()){ + QMutexLocker locker(&mCancelMutex); + if(mCanceled){ + quit(); + }else{ + locker.unlock(); + } + QDir curDir(start.absoluteFilePath()); + foreach(QFileInfo fi, curDir.entryInfoList(QDir::AllEntries | QDir::NoDotAndDotDot)){ + if(fi.isDir()){ + doFsCheck(fi); + }else{ + QString md5sum = Helper::md5Sum(fi.absoluteFilePath()); + mFileQuery->bindValue(":md5sum", md5sum); + mFileQuery->exec(); + while(mFileQuery->next()){ + int count = mFileQuery->value(0).toInt(); + if(count > 0){ + emit consistencyMsg(QString(tr("OK: %1 -> %2")).arg(fi.fileName()).arg(md5sum)); + }else{ + mStrayFilesMutex.lock(); + mStrayFiles << fi.absoluteFilePath(); + mStrayFilesMutex.unlock(); + emit consistencyMsg(QString(tr("NOT FOUND: %1 -> %2")).arg(fi.fileName()).arg(md5sum)); + } + } + } + } + } +} + QString ConsistencyChecker::archivePath(const QString &fileName, const QString &md5sum) const { QString filePath = Helper::createArchivePath(fileName, md5sum); QFileInfo fi(filePath); diff --git a/consistencycheck.h b/consistencycheck.h index 2f934fc..f5470e3 100644 --- a/consistencycheck.h +++ b/consistencycheck.h @@ -12,12 +12,15 @@ #include #include #include +#include +#include class QPushButton; class QPlainTextEdit; class QLabel; class QString; -class QCheckBox; +class QSqlQuery; +class QFileInfo; class ConsistencyChecker; class ConsistencyCheck : public QDialog { @@ -32,15 +35,16 @@ class ConsistencyCheck : public QDialog { void checkerStarted(); void checkerFinished(); void cancelChecker(); - void showErrorsChanged(int state); + void cleanup(); private: + void moveFiles(const QStringList &files); QPushButton *mCancelExit; QPushButton *mCheckDb; QPushButton *mCheckFs; - QPlainTextEdit *mDisplay; - QLabel *mCheckLabel; - QCheckBox *mErrorsOnly; + QPushButton *mCleanup; + QPlainTextEdit *mOkDisplay; + QPlainTextEdit *mErrorDisplay; ConsistencyChecker *mChecker; }; @@ -55,12 +59,18 @@ class ConsistencyChecker : public QThread { int status() const { return mStatus; } void setStatus(int status) { mStatus = status; } void check(); + const QStringList strayFiles(); + void clearStrayFiles(); + const QList strayIds(); + void clearStrayIds(); public slots: void setCancel(bool cancel); + void deleteIds(const QList &ids); private slots: void dbCheck(); + void fsCheck(); signals: void consistencyMsg(const QString &msg); @@ -70,11 +80,17 @@ class ConsistencyChecker : public QThread { private: QString archivePath(const QString &fileName, const QString &md5sum) const; + void doFsCheck(const QFileInfo &start); bool mCanceled; int mMode; int mStatus; QSqlDatabase mDb; + QSqlQuery *mFileQuery; QMutex mCancelMutex; + QMutex mStrayFilesMutex; + QMutex mStrayIdsMutex; + QStringList mStrayFiles; + QList mStrayIds; }; #endif // CONSISTENCYCHECK_H -- cgit v1.2.3-70-g09d2