File indexing completed on 2024-05-12 05:53:56
0001 /* 0002 * Copyright (c) 2018 Sune Vuorela <sune@vuorela.dk> 0003 * 0004 * Permission is hereby granted, free of charge, to any person 0005 * obtaining a copy of this software and associated documentation 0006 * files (the "Software"), to deal in the Software without 0007 * restriction, including without limitation the rights to use, 0008 * copy, modify, merge, publish, distribute, sublicense, and/or sell 0009 * copies of the Software, and to permit persons to whom the 0010 * Software is furnished to do so, subject to the following 0011 * conditions: 0012 * 0013 * The above copyright notice and this permission notice shall be 0014 * included in all copies or substantial portions of the Software. 0015 * 0016 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 0017 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 0018 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 0019 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 0020 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 0021 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 0022 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 0023 * OTHER DEALINGS IN THE SOFTWARE. 0024 */ 0025 0026 #include "scanner.h" 0027 #include <QDirIterator> 0028 #include <future> 0029 #include <QThread> 0030 #include <QStandardItemModel> 0031 0032 Scanner::Scanner(QObject* parent) : QObject(parent), m_running(false) 0033 { 0034 connect(this, &Scanner::dataUpdatedInternal, this, &Scanner::dataUpdated, Qt::QueuedConnection); 0035 } 0036 0037 Scanner::~Scanner() 0038 { 0039 } 0040 0041 void Scanner::doUpdate() 0042 { 0043 if (m_running) { 0044 return; 0045 } 0046 if (!m_rootPath.isEmpty()) { 0047 m_running = true; 0048 m_future = std::async(&Scanner::parseThingsInDifferentThread,this,m_rootPath,QThread::currentThread()); 0049 } 0050 } 0051 0052 void Scanner::parseThingsInDifferentThread(const QString& path, QThread* resultThread) { 0053 0054 QDirIterator it(path, QStringList() << "*.recipe.md", QDir::NoDotAndDotDot | QDir::Files, QDirIterator::Subdirectories); 0055 QMap<QString, QVector<QPair<QString,QString>>> tags; 0056 QMap<QString, QVector<QPair<QString,QString>>> ingredients; 0057 QVector<QPair<QString, QString>> titles; 0058 0059 while (it.hasNext()) { 0060 it.next(); 0061 QFileInfo fi = it.fileInfo(); 0062 QFile f(fi.absoluteFilePath()); 0063 f.open(QIODevice::ReadOnly); 0064 auto parsed = RecipeParser::parseRecipe(&f); 0065 auto pair = qMakePair(parsed.title, fi.absoluteFilePath()); 0066 titles.push_back(pair); 0067 for(const auto& it : qAsConst(parsed.tags)) { 0068 tags[it].push_back(pair); 0069 } 0070 for(const auto& it : qAsConst(parsed.ingredients)) { 0071 auto &list = ingredients[it.ingredient]; 0072 if (!list.contains(pair)) { 0073 list.push_back(pair); 0074 } 0075 } 0076 } 0077 auto recipePairTitleSorter = [] (const auto& r1, const auto& r2) { 0078 return r1.first < r2.first; 0079 }; 0080 0081 auto buildTreeFromMap = [&recipePairTitleSorter] (QMap<QString, QVector<QPair<QString,QString>>> map) { 0082 auto result = std::make_unique<QStandardItemModel>(); 0083 for(auto it = map.constBegin(),end = map.constEnd(); it!=end; it++) { 0084 auto line = std::make_unique<QStandardItem>(it.key()); 0085 auto recipes = it.value(); 0086 std::sort(recipes.begin(), recipes.end(), recipePairTitleSorter); 0087 for(const auto& recipe : qAsConst(recipes)) { 0088 auto child = std::make_unique<QStandardItem>(recipe.first); 0089 child->setData(recipe.second); 0090 line->appendRow(child.release()); 0091 } 0092 result->appendRow(line.release()); 0093 } 0094 return result; 0095 }; 0096 0097 auto parseding = buildTreeFromMap(ingredients); 0098 auto parsedtags = buildTreeFromMap(tags); 0099 std::sort(titles.begin(), titles.end(), recipePairTitleSorter); 0100 auto titlelist = std::make_unique<QStandardItemModel>(); 0101 QHash<QString,QString> titlemap; 0102 for(auto title : qAsConst(titles)) { 0103 titlemap[title.second] = title.first; 0104 auto line = std::make_unique<QStandardItem>(title.first); 0105 line->setData(title.second); 0106 titlelist->appendRow(line.release()); 0107 } 0108 QHash<int, QByteArray> titleListRoleNames; 0109 titleListRoleNames[Qt::DisplayRole] = "display"; 0110 titleListRoleNames[Qt::UserRole +1] = "path"; 0111 titlelist->setItemRoleNames(titleListRoleNames); 0112 0113 parseding->moveToThread(resultThread); 0114 parsedtags->moveToThread(resultThread); 0115 titlelist->moveToThread(resultThread); 0116 std::unique_lock<std::mutex> lock(m_mutex); 0117 m_parsedIngredients = std::move(parseding); 0118 m_parsedTags = std::move(parsedtags); 0119 m_parsedFileNameTitleMap = titlemap; 0120 titlemap = {}; // ensure to decrease refcount 0121 m_titleList = std::move(titlelist); 0122 Q_EMIT dataUpdatedInternal(); 0123 m_running = false; 0124 } 0125 0126 std::shared_ptr<QAbstractItemModel> Scanner::parsedIngredients() const 0127 { 0128 std::unique_lock<std::mutex> lock(m_mutex); 0129 return m_parsedIngredients; 0130 } 0131 0132 std::shared_ptr<QAbstractItemModel> Scanner::parsedTags() const 0133 { 0134 std::unique_lock<std::mutex> lock(m_mutex); 0135 return m_parsedTags; 0136 } 0137 0138 std::shared_ptr<QAbstractItemModel> Scanner::parsedTitleList() const 0139 { 0140 std::unique_lock<std::mutex> lock(m_mutex); 0141 return m_titleList; 0142 } 0143 0144 void Scanner::setRootPath(const QString& path) 0145 { 0146 m_rootPath = path; 0147 doUpdate(); 0148 } 0149 0150 QString Scanner::rootPath() const 0151 { 0152 return m_rootPath; 0153 } 0154 0155 0156 QHash<QString, QString> Scanner::parsedFileNameTitleMap() const 0157 { 0158 std::unique_lock<std::mutex> lock(m_mutex); 0159 return m_parsedFileNameTitleMap; 0160 } 0161 0162 0163