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