/* This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. */ #include #include #include #include #include #include #include #include #include #include #include "dbanalyzer.h" #include "smtreemodel.h" #include "smtreeitem.h" DbAnalyzerDialog::DbAnalyzerDialog(QWidget *parent, Qt::WindowFlags f) : QDialog(parent, f){ //create tab widget mTab = new QTabWidget; //setup analyzer mAnalyzer = new DbAnalyzer(this); //no actors QWidget *noActorsT = new QWidget; QStringList noActorsHeaders = QStringList() << tr("Series") << tr("Part/Subtitle") << tr("Series Part") << tr("Seriespart Id") << tr("Series Id") << tr("Seriespart"); mNoActorsV = new QTreeView; mNoActorsM = new SmTreeModel(noActorsHeaders, this); mNoActorsV->setModel(mNoActorsM); QVBoxLayout *noActorsL = new QVBoxLayout; noActorsL->addWidget(mNoActorsV); mNoActorsV->setColumnHidden(2, true); mNoActorsV->setColumnHidden(3, true); mNoActorsV->setColumnHidden(4, true); mNoActorsV->setColumnHidden(5, true); mNoActorsV->setEditTriggers(QTreeView::NoEditTriggers); mNoActorsV->setSelectionBehavior(QAbstractItemView::SelectRows); mNoActorsV->setSelectionMode(QAbstractItemView::SingleSelection); mNoActorsV->setAlternatingRowColors(true); connect(mNoActorsV, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(noDataDoubleClicked(QModelIndex))); noActorsT->setLayout(noActorsL); //no covers QWidget *noCoversT = new QWidget; QStringList noCoversHeaders = QStringList() << tr("Series") << tr("Part/Subtitle") << tr("Series Part") << tr("Seriespart Id") << tr("Series Id") << tr("Seriespart"); mNoCoversV = new QTreeView; mNoCoversM = new SmTreeModel(noCoversHeaders, this); mNoCoversV->setModel(mNoCoversM); QVBoxLayout *noCoversL = new QVBoxLayout; noCoversL->addWidget(mNoCoversV); mNoCoversV->setColumnHidden(2, true); mNoCoversV->setColumnHidden(3, true); mNoCoversV->setColumnHidden(4, true); mNoCoversV->setColumnHidden(5, true); mNoCoversV->setEditTriggers(QTreeView::NoEditTriggers); mNoCoversV->setSelectionBehavior(QAbstractItemView::SelectRows); mNoCoversV->setSelectionMode(QAbstractItemView::SingleSelection); mNoCoversV->setAlternatingRowColors(true); connect(mNoCoversV, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(noDataDoubleClicked(QModelIndex))); noCoversT->setLayout(noCoversL); //stray actors QWidget *strayActorsT = new QWidget; QStringList strayActorsHeaders = QStringList() << tr("Actor") << tr("Actor Id") << tr("Count"); mStrayActorsV = new QTreeView; mStrayActorsM = new SmTreeModel(strayActorsHeaders, this); mStrayActorsV->setModel(mStrayActorsM); QVBoxLayout *strayActorsL = new QVBoxLayout; strayActorsL->addWidget(mStrayActorsV); mStrayActorsV->setColumnHidden(1, true); mStrayActorsV->setEditTriggers(QTreeView::NoEditTriggers); mStrayActorsV->setSelectionBehavior(QAbstractItemView::SelectRows); mStrayActorsV->setSelectionMode(QAbstractItemView::ExtendedSelection); mStrayActorsV->setAlternatingRowColors(true); strayActorsT->setLayout(strayActorsL); //stray genres QWidget *strayGenresT = new QWidget; QStringList strayGenresHeaders = QStringList() << tr("Genre") << tr("Genre Id") << tr("Count"); mStrayGenresV = new QTreeView; mStrayGenresM = new SmTreeModel(strayGenresHeaders, this); mStrayGenresV->setModel(mStrayGenresM); QVBoxLayout *strayGenresL = new QVBoxLayout; strayGenresL->addWidget(mStrayGenresV); mStrayGenresV->setColumnHidden(1, true); mStrayGenresV->setEditTriggers(QTreeView::NoEditTriggers); mStrayGenresV->setSelectionBehavior(QAbstractItemView::SelectRows); mStrayGenresV->setSelectionMode(QAbstractItemView::ExtendedSelection); mStrayGenresV->setAlternatingRowColors(true); strayGenresT->setLayout(strayGenresL); //buttons mCancel = new QPushButton(tr("Cancel")); connect(mCancel, SIGNAL(clicked()), this, SLOT(cancelAnalyzer())); mRefresh = new QPushButton(tr("Refresh")); connect(mRefresh, SIGNAL(clicked()), this, SLOT(refresh())); mClose = new QPushButton(tr("Close")); connect(mClose, SIGNAL(clicked()), this, SLOT(accept())); mDelete = new QPushButton(tr("Delete...")); mDelete->setEnabled(false); connect(mDelete, SIGNAL(clicked()), this, SLOT(deleteItems())); //setup dialog mTab->addTab(noActorsT, tr("No Actors")); mTab->addTab(noCoversT, tr("No Covers")); mTab->addTab(strayActorsT, tr("Stray actors")); mTab->addTab(strayGenresT, tr("Stray genres")); //buttons QHBoxLayout *buttonLayout = new QHBoxLayout; buttonLayout->addWidget(mCancel); buttonLayout->addWidget(mRefresh); buttonLayout->addStretch(); buttonLayout->addWidget(mDelete); buttonLayout->addWidget(mClose); //totals mTotal = new QLabel(tr("Total items: 0")); mTotal->setAlignment(Qt::AlignCenter); QHBoxLayout *totalLayout = new QHBoxLayout; totalLayout->addWidget(mTotal); //setup dialog QVBoxLayout *mainLayout = new QVBoxLayout; mainLayout->addWidget(mTab); mainLayout->addLayout(totalLayout); mainLayout->addLayout(buttonLayout); setLayout(mainLayout); setMinimumWidth(500); //get things going connect(mTab, SIGNAL(currentChanged(int)), this, SLOT(tabChanged(int))); connect(mAnalyzer, SIGNAL(started()), this, SLOT(analyzerStarted())); connect(mAnalyzer, SIGNAL(finished()), this, SLOT(analyzerFinished())); for(int i = 0; i < mTab->count(); ++i){ mTotals << QString(); } mAnalyzer->start(); } void DbAnalyzerDialog::refresh(){ if(!mAnalyzer->isRunning()){ mAnalyzer->start(); } } void DbAnalyzerDialog::cancelAnalyzer(){ mAnalyzer->setCancel(true); } void DbAnalyzerDialog::analyzerStarted(){ mCancel->setEnabled(true); mRefresh->setEnabled(false); mClose->setEnabled(false); } void DbAnalyzerDialog::analyzerFinished(){ mCancel->setEnabled(false); mRefresh->setEnabled(true); mClose->setEnabled(true); populate(mNoActorsV, mNoActorsM, mAnalyzer->noActors()); populate(mNoCoversV, mNoCoversM, mAnalyzer->noCovers()); populate(mStrayActorsV, mStrayActorsM, mAnalyzer->strayActors()); populate(mStrayGenresV, mStrayGenresM, mAnalyzer->strayGenres()); QString totalString = QString(tr("Total items: %1")); mTotals[0] = totalString.arg(QString::number(mAnalyzer->noActors().count())); mTotals[1] = totalString.arg(QString::number(mAnalyzer->noCovers().count())); mTotals[2] = totalString.arg(QString::number(mAnalyzer->strayActors().count())); mTotals[3] = totalString.arg(QString::number(mAnalyzer->strayGenres().count())); mTotal->setText(mTotals.at(mTab->currentIndex())); } void DbAnalyzerDialog::noDataDoubleClicked(const QModelIndex &idx){ if(mTab->currentIndex() > 1){ return; } if(!idx.isValid()){ return; } const QAbstractItemModel *model = idx.model(); QModelIndex seriesPartIdx = model->index(idx.row(), 5, idx.parent()); QModelIndex seriesIdx = model->index(idx.row(), 4, idx.parent()); emit partClicked(seriesPartIdx.data().toInt(), seriesIdx.data().toInt()); } void DbAnalyzerDialog::deleteItems(){ QTreeView *view = 0; int deleteMode; switch(mTab->currentIndex()){ case 0: case 1: return; break; case 2: view = mStrayActorsV; deleteMode = Actors; break; case 3: view = mStrayGenresV; deleteMode = Genres; break; default: view = 0; break; } Q_ASSERT(view); QModelIndexList selected = view->selectionModel()->selectedRows(1); QList ids; foreach(QModelIndex i, selected){ ids << i.data().toInt(); } emit delItems(deleteMode, ids); } void DbAnalyzerDialog::tabChanged(int index){ mDelete->setEnabled(index > 1); mTotal->setText(mTotals.at(index)); } void DbAnalyzerDialog::populate(QTreeView *view, SmTreeModel *model, const QList > &data){ const int columns = data.first().count(); if(columns == 0){ return; } SmTreeItem *root = new SmTreeItem(columns); foreach(QList l, data){ SmTreeItem *child = new SmTreeItem(l, root); root->appendChild(child); } model->setRoot(root); view->resizeColumnToContents(0); } DbAnalyzer::DbAnalyzer(QObject *parent) : QThread(parent), mCanceled(false), mStatus(Fail) { mDb = QSqlDatabase::cloneDatabase(QSqlDatabase::database("treedb"), "analyzerDb"); mDb.open(); mStatus = mDb.isOpen() ? Ok : Fail; mNoActorQuery = new QSqlQuery(mDb); mNoActorQuery->prepare("SELECT series.tseries_name, seriesparts.iseriespart, seriesparts.tsubtitle, series.iseries_id, seriesparts.iseriesparts_id FROM series, seriesparts LEFT JOIN seriesparts_actormap ON seriesparts.iseriesparts_id = seriesparts_actormap.iseriesparts_id WHERE iactors_id IS NULL AND seriesparts.iseries_id = series.iseries_id ORDER BY tseries_name"); mStrayActorsQuery = new QSqlQuery(mDb); mStrayActorsQuery->prepare("SELECT actors.tactorname, actors.iactors_id, COUNT(seriesparts_actormap.iactors_id) FROM actors LEFT JOIN seriesparts_actormap ON actors.iactors_id = seriesparts_actormap.iactors_id WHERE seriesparts_actormap.iactors_id IS NULL GROUP BY actors.iactors_id, actors.tactorname ORDER BY actors.tactorname"); mStrayGenresQuery = new QSqlQuery(mDb); mStrayGenresQuery->prepare("SELECT genres.tgenrename, genres.igenres_id, COUNT(seriesparts_genremap.igenres_id) FROM genres LEFT JOIN seriesparts_genremap ON genres.igenres_id = seriesparts_genremap.igenres_id WHERE seriesparts_genremap.igenres_id IS NULL GROUP BY genres.igenres_id, genres.tgenrename ORDER BY genres.tgenrename"); mNoCoverQuery = new QSqlQuery(mDb); mNoCoverQuery->prepare("SELECT series.tseries_name, seriesparts.iseriespart, seriesparts.tsubtitle, series.iseries_id, seriesparts.iseriesparts_id FROM files, series, seriesparts WHERE files.iseriespart_id = seriesparts.iseriesparts_id AND seriesparts.iseries_id = series.iseries_id GROUP BY files.iseriespart_id, series.tseries_name,seriesparts.iseriespart, seriesparts.tsubtitle, seriesparts.iseriesparts_id, series.iseries_id HAVING COUNT(iseriespart_id) = 1 ORDER BY series.tseries_name"); } DbAnalyzer::~DbAnalyzer(){ delete mNoActorQuery; delete mNoCoverQuery; delete mStrayActorsQuery; delete mStrayGenresQuery; mDb.close(); mDb = QSqlDatabase(); QSqlDatabase::removeDatabase("analyzerDb"); } void DbAnalyzer::setCancel(bool canceled){ QMutexLocker m(&mCancelMutex); mCanceled = canceled; } void DbAnalyzer::run(){ mNoActorR = noDataCheck(mNoActorQuery); mNoCoverR = noDataCheck(mNoCoverQuery); mStrayActorR = strayCheck(mStrayActorsQuery); mStrayGenresR = strayCheck(mStrayGenresQuery); } const QList > DbAnalyzer::noDataCheck(QSqlQuery *query){ if(!query->exec()){ QMutexLocker m(&mStatusMutex); mStatus = Fail; return QList >(); } QList > retval; while(query->next()){ if(mCanceled){ break; } QList res; res << query->value(0); if(!query->value(2).isNull()){ res << query->value(2); }else{ res << query->value(1); } res << query->value(1) << query->value(2) << query->value(3) << query->value(4); retval << res; } return retval; } const QList > DbAnalyzer::strayCheck(QSqlQuery *query){ if(!query->exec()){ QMutexLocker m(&mStatusMutex); mStatus = Fail; return QList >(); } QList > retval; while(query->next()){ if(mCanceled){ break; } QList res; res << query->value(0) << query->value(1) << query->value(2); retval << res; } return retval; }