/* 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 "picfilesmodel.h" #include "smglobals.h" #include "smtreeitem.h" #include "helper.h" PicFilesModel::PicFilesModel(const QStringList &headers, QObject *parent) : SmTreeModel(headers, parent) { //conjure up model mMappingTreeModel = static_cast(SmGlobals::instance()->model("MappingTree")); //setup database mDb = QSqlDatabase::database("treedb"); mPopulateQS = QString("SELECT DISTINCT(pics.ipicsid), pics.tfilename, pics.isize, pics.tformat, pics.dtadded, pics.cmd5sum, pics.cpicsize FROM pics, pics_mappings WHERE pics_mappings.imappings_parents_id IN (%1) AND pics_mappings.ipics_id = pics.ipicsid ORDER BY pics.tfilename"); mMappingsQS = QString("SELECT DISTINCT(pics_mappings.imappings_parents_id) FROM pics_mappings WHERE pics_mappings.ipics_id IN (%1)"); } void PicFilesModel::setMapping(int pMapId){ QList ids = mMappingTreeModel->childList(pMapId, MappingTreeModel::MappingId); mMappingIds.clear(); foreach(QVariant i, ids){ mMappingIds << i.toInt(); } } QVariant PicFilesModel::data(const QModelIndex &index, int role) const { if(role == Qt::FontRole){ if(index.column() == Md5Sum){ return QFont("Monospace"); } } if(role == Qt::ForegroundRole){ if(index.column() == Size){ int fileSize = index.data(SizeRole).toInt(); if(fileSize > 1024 * 1024 * 1024){ return QColor(Qt::red); }else{ return QColor(Qt::green); } } } SmTreeItem *item = itemAt(index); if(role == FileNameRole){ return item->data(FileName); } if(role == SizeRole){ return item->data(Size); } if(role == MimeTypeRole){ return item->data(MimeType); } if(role == FullPathRole){ return item->data(FullPath); } if(role == IdRole){ return item->data(Id); } if(role == AddedRole){ return item->data(Added); } if(role == Md5SumRole){ return item->data(Md5Sum); } if(role == PicSizeRole){ return item->data(PicSize); } return SmTreeModel::data(index, role); } QList PicFilesModel::dataList(const QModelIndex &idx){ QList retval; if(!idx.isValid()){ return retval; } SmTreeItem *item = itemAt(idx); for(int i = 0; i < NumFields; ++i){ retval << item->data(i); } QList mapData = mappingDataFromFile(idx.data(PicFilesModel::IdRole).toInt()); QList mappings; foreach(MappingData d, mapData){ mappings << d.path; } retval.append(QVariant::fromValue(mappings)); return retval; } QList > PicFilesModel::allFiles() const { QList > retval; //assume depth of exactly 1 SmTreeItem *rootItem = root(); for(int i = 0; i < rootItem->childCount(); ++i){ SmTreeItem *cur = rootItem->child(i); QList data; for(int j = 0; j < cur->columnCount(); ++j){ data << cur->data(j); } retval << data; } return retval; } void PicFilesModel::removeFiles(const QList > &files){ QSqlQuery deleteFile(mDb); deleteFile.prepare("DELETE FROM pics WHERE ipicsid = :id"); QList success; // remove from model for(int i = 0; i < files.size(); ++i){ QPair cur = files.at(i); deleteFile.bindValue(":id", cur.first); if(deleteFile.exec()){ success << cur.first; QFile::remove(cur.second); } } // remove from view foreach(int id, success){ QModelIndex idx = find(id, Id); if(idx.isValid()){ removeRows(idx.row(), 1, QModelIndex()); } } } bool PicFilesModel::changeMappings(const QList &fileIds, const QList &parentIds){ mDb.transaction(); QSqlQuery deleteMappingsQ(mDb); deleteMappingsQ.prepare("DELETE FROM pics_mappings WHERE ipics_id = :id"); QSqlQuery addMappingsQ(mDb); addMappingsQ.prepare("INSERT INTO pics_mappings(ipics_id, imappings_parents_id) VALUES(:pid, :id)"); foreach(int fid, fileIds){ deleteMappingsQ.bindValue(":id", fid); if(!deleteMappingsQ.exec()){ goto error; } foreach(QVariant pid, parentIds){ addMappingsQ.bindValue(":pid", fid); addMappingsQ.bindValue(":id", pid); if(!addMappingsQ.exec()){ goto error; } } } mDb.commit(); return true; error: mDb.rollback(); return false; } SmTreeItem *PicFilesModel::mappingTreeFromFile(int fileId){ QList fileIds = QList() << fileId; QList parentIds = mappingPIdsFromFiles(fileIds); if(parentIds.isEmpty()){ return 0; } SmTreeItem *retval = new SmTreeItem(2); QSqlQuery mpq(mDb); mpq.prepare("SELECT imapping_parents_id, iparent_id, tdescription_name, mapping_parents.idescription_id FROM mapping_parents, mapping_description WHERE imapping_parents_id = :id AND mapping_parents.idescription_id = mapping_description.idescription_id"); foreach(int pId, parentIds){ QList curData; int curParent = -1; mpq.bindValue(":id", pId); mpq.exec(); while(mpq.next()){ QVariantList d; d << mpq.value(2) << mpq.value(0); curData << d; curParent = mpq.value(1).toInt(); } while(curParent != -1){ mpq.bindValue(":id", curParent); mpq.exec(); while(mpq.next()){ QVariantList d; d << mpq.value(2) << mpq.value(0); curData << d; curParent = mpq.value(1).toInt(); } } std::reverse(curData.begin(), curData.end()); SmTreeItem *parentItem = retval; for(int i = 0; i < curData.size(); ++i){ QVariantList data = curData.at(i); SmTreeItem *searchItem = findRecursive(parentItem, data.at(0), data.at(1)); if(searchItem){ parentItem = searchItem; }else{ int where; for(where = 0; where < parentItem->childCount(); ++where){ SmTreeItem *c = parentItem->child(where); if(c->data(0).toString() > data.at(0).toString()){ break; } } SmTreeItem *newChild = new SmTreeItem(data, parentItem); parentItem->insertChild(where, newChild); parentItem = newChild; } } } return retval; } SmTreeItem *PicFilesModel::findRecursive(SmTreeItem *start, const QVariant &name, const QVariant id) const{ if(!start){ return 0; } if(start->data(0) == name && start->data(1) == id){ return start; } for(int i = 0; i < start->childCount(); ++i){ SmTreeItem *child = start->child(i); if(child->data(0) == name && child->data(1) == id){ return child; } if(child->childCount()){ return findRecursive(child, name, id); } } SmTreeItem *next = start->next(); if(next){ return findRecursive(next, name, id); } return 0; } QList PicFilesModel::mappingDataFromFile(int fileId){ QList d = QList() << fileId; return mappingDataFromFiles(d); } QList PicFilesModel::mappingDataFromFiles(const QList fileIds){ QList parentIds = mappingPIdsFromFiles(fileIds); if(parentIds.isEmpty()){ return QList(); } QList retval; QSqlQuery mpq(mDb); mpq.prepare("SELECT imapping_parents_id, iparent_id, tdescription_name, mapping_parents.idescription_id FROM mapping_parents, mapping_description WHERE imapping_parents_id = :id AND mapping_parents.idescription_id = mapping_description.idescription_id"); foreach(int pId, parentIds){ MappingData curData; int curParent = -1; mpq.bindValue(":id", pId); mpq.exec(); while(mpq.next()){ curData.name = mpq.value(2).toString(); curData.mappingId = pId; curData.descId = mpq.value(3).toInt(); curData.parents << pId; curData.path << curData.name; curParent = mpq.value(1).toInt(); } while(curParent != -1){ mpq.bindValue(":id", curParent); mpq.exec(); while(mpq.next()){ curData.parents << curParent; curData.path << mpq.value(2).toString(); curParent = mpq.value(1).toInt(); } } std::reverse(curData.parents.begin(), curData.parents.end()); std::reverse(curData.path.begin(), curData.path.end()); retval << curData; } return retval; } QList PicFilesModel::mappingPIdsFromFiles(QList fileIds){ if(fileIds.isEmpty()){ return QList(); } QStringList idList; foreach(int id, fileIds){ idList << QString::number(id); } QString qTempl = QString(mMappingsQS.arg(idList.join(","))); QSqlQuery q(mDb); q.prepare(qTempl); if(!q.exec()){ mDb.close(); mDb.open(); return QList(); } QList retval; while(q.next()){ retval << q.value(0).toInt(); } return retval; } void PicFilesModel::selectFromAll(){ mAllPics.clear(); QSqlQuery q("SELECT tfilename, cmd5sum, ipicsid FROM pics", mDb); while(q.next()){ QList cur; cur << q.value(0) << q.value(1) << q.value(2); mAllPics << cur; } mCurrentPics = &mAllPics; } void PicFilesModel::selectFromRecent(int days){ mAllPics.clear(); QString qTempl = QString("SELECT tfilename, cmd5sum, ipicsid FROM pics WHERE dtadded > now() - interval '%1 days'").arg(QString::number(days)); QSqlQuery q(qTempl, mDb); while(q.next()){ QList cur; cur << q.value(0) << q.value(1) << q.value(2); mAllPics << cur; } mCurrentPics = &mAllPics; } void PicFilesModel::selectFromSelection(QList ids){ mAllPics.clear(); QStringList idQS; foreach (QVariant i, ids){ idQS << QString::number(i.toInt()); } QString inPart = idQS.join(','); QString qTempl = QString("SELECT tfilename, cmd5sum, ipicsid FROM pics, pics_mappings WHERE pics_mappings.imappings_parents_id IN (%1) AND pics_mappings.ipics_id = pics.ipicsid").arg(inPart); QSqlQuery q(qTempl, mDb); while(q.next()){ QList cur; cur << q.value(0) << q.value(1) << q.value(2); mAllPics << cur; } mCurrentPics = &mAllPics; } // delete this func once the one above works void PicFilesModel::allPicIds(){ mAllPics.clear(); mCurrentBatch = 0; QSqlQuery q("SELECT tfilename, cmd5sum, ipicsid FROM pics", mDb); while(q.next()){ QList cur; cur << q.value(0) << q.value(1) << q.value(2); mAllPics << cur; } unsigned seed = std::chrono::system_clock::now().time_since_epoch().count(); std::shuffle(mAllPics.begin(), mAllPics.end(), std::default_random_engine(seed)); mCurrentPics = &mAllPics; } void PicFilesModel::recentPicIds(){ mRecentPics.clear(); mCurrentBatch = 0; QSqlQuery q("SELECT tfilename, cmd5sum, ipicsid FROM pics WHERE dtadded > now() - interval '6 months'", mDb); while(q.next()){ QList cur; cur << q.value(0) << q.value(1) << q.value(2); mRecentPics << cur; } unsigned seed = std::chrono::system_clock::now().time_since_epoch().count(); std::shuffle(mRecentPics.begin(), mRecentPics.end(), std::default_random_engine(seed)); mCurrentPics = &mRecentPics; } QList > PicFilesModel::getNextBatch(){ if(mCurrentBatch * 100 >= mAllPics.count()){ mCurrentBatch = 0; } int start = mCurrentBatch * 100; QList > res = mCurrentPics->mid(start, 100); QList > retval; for(QList >::const_iterator it = res.constBegin(); it != res.constEnd(); ++it){ QList c = *it; QList item; item << Helper::createArchivePath(c.value(0).toString(), c.value(1).toString()) << c.value(2); retval << item; } ++mCurrentBatch; return retval; } void PicFilesModel::populate(){ SmTreeItem *root = new SmTreeItem(NumFields); QStringList idList; foreach(int id, mMappingIds){ idList << QString::number(id); } QString query = mPopulateQS.arg(idList.join(",")); QSqlQuery q(query, mDb); while(q.next()){ QList data; data << q.value(1) << q.value(2) << q.value(3); // Filename, size and Mime type data << Helper::createArchivePath(q.value(1).toString(), q.value(5).toString()); // full path; data << q.value(0) << q.value(4) << q.value(5); //Id, added and md5 data << q.value(6); // picsize SmTreeItem *child = new SmTreeItem(data, root); root->appendChild(child); } setRoot(root); }