File indexing completed on 2024-05-12 17:24:10

0001 // SPDX-FileCopyrightText: 2023 Mathis <mbb@kaidan.im>
0002 // SPDX-License-Identifier: LGPL-2.0-or-later
0003 
0004 #include "plantsmodel.h"
0005 #include <QCoroTask>
0006 #include <QCoroFuture>
0007 #include <QDateTime>
0008 
0009 #include <unordered_map>
0010 #include <algorithm>
0011 
0012 using namespace DB;
0013 
0014 PlantsModel::PlantsModel(QObject *parent)
0015     : QAbstractListModel(parent)
0016 {
0017     auto future = Database::instance().plants();
0018 
0019     QCoro::connect(std::move(future), this, [this](auto &&plants) {
0020         beginResetModel();
0021         m_data = plants;
0022         endResetModel();
0023         Q_EMIT summaryChanged();
0024     });
0025 
0026     connect(&Database::instance(), &Database::plantChanged, this, [this](DB::Plant::Id plantId) {
0027         const auto it = std::find_if(m_data.cbegin(), m_data.cend(), [plantId](const auto &plant) {
0028             return plantId == plant.plant_id;
0029         });
0030 
0031         if (it == m_data.cend()) {
0032             return;
0033         }
0034 
0035         const int row = it - m_data.cbegin();
0036 
0037         auto future = Database::instance().plant(plantId);
0038 
0039         QCoro::connect(std::move(future), this, [this, row](auto &&plant) {
0040             if (plant) {
0041                 m_data[row] = plant.value();
0042                 const auto idx = index(row, 0);
0043                 Q_EMIT dataChanged(idx, idx);
0044                 Q_EMIT summaryChanged();
0045             }
0046         });
0047     });
0048 }
0049 
0050 int PlantsModel::rowCount(const QModelIndex &) const
0051 {
0052     return m_data.size();
0053 }
0054 
0055 QHash<int, QByteArray> PlantsModel::roleNames() const
0056 {
0057     return {
0058         {Role::PlantID, "plantId"},
0059         {Role::Name, "name" },
0060         {Role::Species, "species"},
0061         {Role::ImgUrl, "imgUrl"},
0062         {Role::WaterInterval, "waterInterval"},
0063         {Role::Location, "location"},
0064         {Role::DateOfBirth, "dateOfBirth"},
0065         {Role::LastWatered, "lastWatered"},
0066         {Role::WantsToBeWateredIn, "wantsToBeWateredIn"},
0067         {Role::CurrentHealth, "currentHealth"},
0068     };
0069 }
0070 
0071 QVariant PlantsModel::data(const QModelIndex &index, int role) const
0072 {
0073     Q_ASSERT(checkIndex(index, QAbstractItemModel::CheckIndexOption::IndexIsValid));
0074 
0075     const auto plant = m_data.at(index.row());
0076 
0077     static std::unordered_map<int,std::vector<QDateTime>> waterEvents;
0078 
0079     switch(role){
0080         case Role::PlantID:
0081             return plant.plant_id;
0082         case Role::Name:
0083             return plant.name;
0084         case Role::Species:
0085             return plant.species;
0086         case Role::ImgUrl:
0087             return plant.img_url;
0088         case Role::WaterInterval:
0089             return plant.water_intervall;
0090         case Role::Location:
0091             return plant.location;
0092         case Role::DateOfBirth:
0093             return QDateTime::fromSecsSinceEpoch(plant.date_of_birth).date();
0094         case Role::LastWatered:
0095             return QDateTime::fromSecsSinceEpoch(plant.last_watered).date();
0096         case Role::WantsToBeWateredIn:
0097             return QDate::currentDate().daysTo(QDateTime::fromSecsSinceEpoch(plant.last_watered).date().addDays(plant.water_intervall));
0098         case Role::CurrentHealth:
0099             return plant.current_health;
0100     };
0101 
0102     Q_UNREACHABLE();
0103 }
0104 
0105 void PlantsModel::addPlant(const QString &name, const QString &species, const QString &imgUrl, const int waterInterval, const QString location, const int dateOfBirth, const int health)
0106 {
0107     const int now = QDateTime::currentDateTime().toSecsSinceEpoch();
0108     auto future = Database::instance().addPlant(name, species, imgUrl, waterInterval, location, dateOfBirth, now, now, health);
0109 
0110     QCoro::connect(std::move(future), this, [=, this](auto &&result) {
0111         beginInsertRows({}, m_data.size(), m_data.size());
0112         m_data.push_back(Plant{result, name, species, imgUrl, waterInterval, location, dateOfBirth, 1, now, now, health});
0113         endInsertRows();
0114     });
0115 }
0116 
0117 void PlantsModel::editPlant(const DB::Plant::Id plantId, const QString &name, const QString &species, const QString &imgUrl, const int waterIntervall, const QString location, const int dateOfBirth)
0118 {
0119     const int row = [&]() {
0120         const auto it = std::find_if(m_data.cbegin(), m_data.cend(), [plantId](const auto &plant) {
0121             return plantId == plant.plant_id;
0122         });
0123 
0124         Q_ASSERT(it != m_data.cend());
0125 
0126         return it - m_data.cbegin();
0127     }();
0128 
0129     Database::instance().editPlant(plantId, name, species, imgUrl, waterIntervall, location, dateOfBirth);
0130 
0131     const auto idx = index(row, 0);
0132 
0133     auto &plant = m_data[row];
0134     plant.name = name;
0135     plant.species = species;
0136     plant.img_url = imgUrl;
0137     plant.water_intervall = waterIntervall;
0138     plant.location = location;
0139     plant.date_of_birth = dateOfBirth;
0140 
0141     emit dataChanged(idx, idx);
0142 }
0143 
0144 void PlantsModel::deletePlant(const int plantId)
0145 {
0146     const int row = [&]() {
0147         const auto it = std::find_if(m_data.cbegin(), m_data.cend(), [plantId](const auto &plant) {
0148             return plantId == plant.plant_id;
0149         });
0150 
0151         Q_ASSERT(it != m_data.cend());
0152 
0153         return it - m_data.cbegin();
0154     }();
0155 
0156     m_data.erase(m_data.begin() + row);
0157 
0158     beginRemoveRows({}, row, row);
0159     Database::instance().deletePlant(plantId);
0160     endRemoveRows();
0161 }
0162 
0163 PlantsModel::Summary PlantsModel::summary() const
0164 {
0165     auto needsWatering = [](const Plant &plant) {
0166         auto lastWatered = QDateTime::fromSecsSinceEpoch(plant.last_watered).date();
0167         auto daysTillWatering = QDate::currentDate().daysTo(lastWatered.addDays(plant.water_intervall));
0168         return daysTillWatering <= 0;
0169     };
0170 
0171     if (std::any_of(m_data.begin(), m_data.end(), needsWatering)) {
0172         return Summary::SomeNeedWater;
0173     }
0174 
0175     return Summary::NothingToDo;
0176 }