File indexing completed on 2025-01-05 04:59:41

0001 /*
0002  * SPDX-FileCopyrightText: 2014 Kevin Ottens <ervin@kde.org>
0003  * SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
0004  */
0005 
0006 
0007 #include "akonaditaskqueries.h"
0008 
0009 #include "utils/datetime.h"
0010 
0011 #include <QTimer>
0012 
0013 using namespace Akonadi;
0014 
0015 TaskQueries::TaskQueries(const StorageInterface::Ptr &storage,
0016                          const SerializerInterface::Ptr &serializer,
0017                          const MonitorInterface::Ptr &monitor,
0018                          const Cache::Ptr &cache)
0019     : m_serializer(serializer),
0020       m_monitor(monitor),
0021       m_cache(cache),
0022       m_helpers(new LiveQueryHelpers(serializer, storage)),
0023       m_integrator(new LiveQueryIntegrator(serializer, monitor)),
0024       m_workdayPollTimer(new QTimer(this))
0025 {
0026     m_workdayPollTimer->setInterval(30000);
0027     connect(m_workdayPollTimer, &QTimer::timeout, this, &TaskQueries::onWorkdayPollTimeout);
0028 
0029     m_integrator->addRemoveHandler([this] (const Item &item) {
0030         m_findChildren.remove(item.id());
0031         m_findContexts.remove(item.id());
0032     });
0033 
0034     connect(m_monitor.data(), &MonitorInterface::itemChanged, this, [this] (const Item &item) {
0035         const auto it = m_findContexts.find(item.id());
0036         if (it == m_findContexts.end())
0037             return;
0038 
0039         m_findContextsItem[item.id()] = item;
0040         (*it)->reset();
0041     });
0042 }
0043 
0044 int TaskQueries::workdayPollInterval() const
0045 {
0046     return m_workdayPollTimer->interval();
0047 }
0048 
0049 void TaskQueries::setWorkdayPollInterval(int interval)
0050 {
0051     m_workdayPollTimer->setInterval(interval);
0052 }
0053 
0054 TaskQueries::TaskResult::Ptr TaskQueries::findAll() const
0055 {
0056     auto fetch = m_helpers->fetchItems(const_cast<TaskQueries*>(this));
0057     auto predicate = [this] (const Akonadi::Item &item) {
0058         return m_serializer->isTaskItem(item);
0059     };
0060     m_integrator->bind("TaskQueries::findAll", m_findAll, fetch, predicate);
0061     return m_findAll->result();
0062 }
0063 
0064 TaskQueries::TaskResult::Ptr TaskQueries::findChildren(Domain::Task::Ptr task) const
0065 {
0066     Akonadi::Item item = m_serializer->createItemFromTask(task);
0067     auto &query = m_findChildren[item.id()];
0068     auto fetch = m_helpers->fetchSiblings(item, const_cast<TaskQueries*>(this));
0069     auto predicate = [this, task] (const Akonadi::Item &childItem) {
0070         return m_serializer->isTaskChild(task, childItem);
0071     };
0072     m_integrator->bind("TaskQueries::findChildren", query, fetch, predicate);
0073     return query->result();
0074 }
0075 
0076 TaskQueries::ProjectResult::Ptr TaskQueries::findProject(Domain::Task::Ptr task) const
0077 {
0078     Akonadi::Item childItem = m_serializer->createItemFromTask(task);
0079     auto &query = m_findProject[childItem.id()];
0080     auto fetch = m_helpers->fetchTaskAndAncestors(task, const_cast<TaskQueries*>(this));
0081     auto predicate = [this, childItem] (const Akonadi::Item &item) {
0082         return m_serializer->isProjectItem(item);
0083     };
0084     auto compare = [] (const Akonadi::Item &item1, const Akonadi::Item &item2) {
0085         return item1.id() == item2.id();
0086     };
0087     m_integrator->bindRelationship("TaskQueries::findProject", query, fetch, compare, predicate);
0088     return query->result();
0089 }
0090 
0091 TaskQueries::DataSourceResult::Ptr TaskQueries::findDataSource(Domain::Task::Ptr task) const
0092 {
0093     Akonadi::Item item = m_serializer->createItemFromTask(task);
0094     auto &query = m_findDataSource[item.id()];
0095     auto fetch = m_helpers->fetchItemCollection(item, const_cast<TaskQueries*>(this));
0096     auto predicate = [] (const Akonadi::Collection &) { return true; };
0097 
0098     m_integrator->bind("TaskQueries::findDataSource", query, fetch, predicate);
0099     return query->result();
0100 }
0101 
0102 TaskQueries::TaskResult::Ptr TaskQueries::findTopLevel() const
0103 {
0104     Q_ASSERT(m_cache);
0105     auto fetch = m_helpers->fetchItems(const_cast<TaskQueries*>(this));
0106     auto predicate = [this] (const Akonadi::Item &item) {
0107         // Tasks with no parent, or whose parent is a project (not a task)
0108         if (!m_serializer->isTaskItem(item))
0109             return false;
0110 
0111         const auto items = m_cache->items(item.parentCollection());
0112         auto currentItem = item;
0113         auto parentUid = m_serializer->relatedUidFromItem(currentItem);
0114         while (!parentUid.isEmpty()) {
0115             const auto parent = std::find_if(items.cbegin(), items.cend(),
0116                                              [this, parentUid] (const Akonadi::Item &item) {
0117                                                  return m_serializer->itemUid(item) == parentUid;
0118                                              });
0119             if (parent == items.cend())
0120                 break;
0121 
0122             if (m_serializer->isTaskItem(*parent))
0123                 return false;
0124 
0125             currentItem = *parent;
0126             parentUid = m_serializer->relatedUidFromItem(currentItem);
0127         }
0128         return true;
0129     };
0130     m_integrator->bind("TaskQueries::findTopLevel", m_findTopLevel, fetch, predicate);
0131     return m_findTopLevel->result();
0132 }
0133 
0134 TaskQueries::TaskResult::Ptr TaskQueries::findInboxTopLevel() const
0135 {
0136     auto fetch = m_helpers->fetchItems(const_cast<TaskQueries*>(this));
0137     auto predicate = [this] (const Akonadi::Item &item) {
0138         // Tasks without a parent (neither task nor project)
0139         return m_serializer->isTaskItem(item) && m_serializer->relatedUidFromItem(item).isEmpty();
0140     };
0141     m_integrator->bind("TaskQueries::findInboxTopLevel", m_findInboxTopLevel, fetch, predicate);
0142     return m_findInboxTopLevel->result();
0143 }
0144 
0145 TaskQueries::TaskResult::Ptr TaskQueries::findWorkdayTopLevel() const
0146 {
0147     if (!m_findWorkdayTopLevel) {
0148         m_workdayPollTimer->start();
0149         m_today = Utils::DateTime::currentDate();
0150     }
0151 
0152     auto fetch = m_helpers->fetchItems(const_cast<TaskQueries*>(this));
0153     auto isWorkdayItem = [this] (const Akonadi::Item &item) {
0154         if (!m_serializer->isTaskItem(item))
0155             return false;
0156 
0157         const Domain::Task::Ptr task = m_serializer->createTaskFromItem(item);
0158 
0159         const QDate doneDate = task->doneDate();
0160         const QDate startDate = task->startDate();
0161         const QDate dueDate = task->dueDate();
0162         const QDate today = Utils::DateTime::currentDate();
0163 
0164         const bool pastStartDate = startDate.isValid() && startDate <= today;
0165         const bool pastDueDate = dueDate.isValid() && dueDate <= today;
0166         const bool todayDoneDate = doneDate == today;
0167 
0168         if (task->isDone())
0169             return todayDoneDate;
0170         else
0171             return pastStartDate || pastDueDate;
0172     };
0173     auto predicate = [this, isWorkdayItem] (const Akonadi::Item &item) {
0174         if (!isWorkdayItem(item))
0175             return false;
0176 
0177         const auto items = m_cache->items(item.parentCollection());
0178         auto currentItem = item;
0179         auto parentUid = m_serializer->relatedUidFromItem(currentItem);
0180         while (!parentUid.isEmpty()) {
0181             const auto parent = std::find_if(items.cbegin(), items.cend(),
0182                                              [this, parentUid] (const Akonadi::Item &item) {
0183                                                  return m_serializer->itemUid(item) == parentUid;
0184                                              });
0185             if (parent == items.cend())
0186                 break;
0187 
0188             if (isWorkdayItem(*parent))
0189                 return false;
0190 
0191             currentItem = *parent;
0192             parentUid = m_serializer->relatedUidFromItem(currentItem);
0193         }
0194 
0195         return true;
0196     };
0197     m_integrator->bind("TaskQueries::findWorkdayTopLevel", m_findWorkdayTopLevel, fetch, predicate);
0198     return m_findWorkdayTopLevel->result();
0199 }
0200 
0201 TaskQueries::ContextResult::Ptr TaskQueries::findContexts(Domain::Task::Ptr task) const
0202 {
0203     Akonadi::Item taskItem = m_serializer->createItemFromTask(task);
0204     const auto taskItemId = taskItem.id();
0205     m_findContextsItem[taskItemId] = taskItem;
0206 
0207     auto &query = m_findContexts[taskItemId];
0208     auto fetch = m_helpers->fetchItems(const_cast<TaskQueries*>(this));
0209     auto predicate = [this, taskItemId] (const Akonadi::Item &contextItem) {
0210         auto context = m_serializer->createContextFromItem(contextItem);
0211         if (!context)
0212             return false;
0213 
0214         const auto taskItem = m_findContextsItem[taskItemId];
0215         return m_serializer->isContextChild(context, taskItem);
0216     };
0217     m_integrator->bind("TaskQueries::findContexts", query, fetch, predicate);
0218     return query->result();
0219 }
0220 
0221 void TaskQueries::onWorkdayPollTimeout()
0222 {
0223     auto newDate = Utils::DateTime::currentDate();
0224     if (m_findWorkdayTopLevel && m_today != newDate) {
0225         m_today = newDate;
0226         m_findWorkdayTopLevel->reset();
0227     }
0228 }
0229 
0230 #include "moc_akonaditaskqueries.cpp"