diff --git a/CMakeLists.txt b/CMakeLists.txt index ec6dc0a..6f599c2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -29,6 +29,7 @@ add_library(${TARGET_APP} STATIC model/commands/removerowscommand.h model/commands/removerowscommand.cpp model/commands/edititemcommand.h model/commands/edititemcommand.cpp data/filehandler.h data/filehandler.cpp + model/metadata.h ) include_directories(${CMAKE_CURRENT_BINARY_DIR}) diff --git a/formats/jsonparser.cpp b/formats/jsonparser.cpp index 10ba77a..075f607 100644 --- a/formats/jsonparser.cpp +++ b/formats/jsonparser.cpp @@ -3,7 +3,7 @@ #include #include -#include "../model/tablemodel.h" +#include "../model/metadata.h" QList> JsonParser::toItemValuesList(const QByteArray& jsonData, const QString& objectName) { @@ -23,22 +23,37 @@ QList> JsonParser::toItemValuesList(const QByteArray& jsonD return result; } -QHash JsonParser::jsonObjectToItemValues(const QJsonObject& itemJsonObject) { - QHash values; +QByteArray JsonParser::itemValuesListToJson(const QList>& itemValuesList, + const QString& objectName) { + QJsonDocument jsonDoc; + QJsonObject rootObject; + QJsonArray itemArray; + for (const QHash& itemValues : itemValuesList) { + QJsonObject itemObject; - // TODO make this more generic (by reading from model meta data) - values[TableModel::NameRole] = - itemJsonObject[TableModel::ROLE_NAMES.value(TableModel::NameRole)].toString(); - values[TableModel::DescriptionRole] = - itemJsonObject[TableModel::ROLE_NAMES.value(TableModel::DescriptionRole)].toString(); - values[TableModel::InfoRole] = - itemJsonObject[TableModel::ROLE_NAMES.value(TableModel::InfoRole)].toString(); - values[TableModel::AmountRole] = - itemJsonObject[TableModel::ROLE_NAMES.value(TableModel::AmountRole)].toInt(); - values[TableModel::FactorRole] = - itemJsonObject[TableModel::ROLE_NAMES.value(TableModel::FactorRole)].toDouble(); + QListIterator i(USER_FACING_ROLES); + while (i.hasNext()) { + const UserRoles role = i.next(); + const QString roleName = ROLE_NAMES.value(role); + const QVariant value = itemValues.value(role); + if (STRING_ROLES.contains(role)) { + itemObject.insert(roleName, value.toString()); + } else if (INT_ROLES.contains(role)) { + itemObject.insert(roleName, value.toInt()); + } else if (DOUBLE_ROLES.contains(role)) { + itemObject.insert(roleName, value.toDouble()); + } else { + qCritical() << QString("Can't find data type for role %1!!!").arg(role); + } + } - return values; + itemArray.append(itemObject); + } + + rootObject.insert(objectName, itemArray); + jsonDoc.setObject(rootObject); + + return jsonDoc.toJson(QJsonDocument::Compact); } JsonParser::JsonParser() {} @@ -56,3 +71,30 @@ QJsonArray JsonParser::extractItemArray(const QByteArray& jsonData, const QStrin return itemArray; } + +QHash JsonParser::jsonObjectToItemValues(const QJsonObject& itemJsonObject) { + QHash values; + + QListIterator i(USER_FACING_ROLES); + while (i.hasNext()) { + const UserRoles role = i.next(); + std::pair keyValuePair = getKeyValuePair(itemJsonObject, role); + values.insert(keyValuePair.first, keyValuePair.second); + } + return values; +} + +pair JsonParser::getKeyValuePair(const QJsonObject& itemJsonObject, const int role) { + QVariant result; + const QJsonValue jsonValue = itemJsonObject[ROLE_NAMES.value(role)]; + if (STRING_ROLES.contains(role)) { + result = jsonValue.toString(); + } else if (INT_ROLES.contains(role)) { + result = jsonValue.toInt(); + } else if (DOUBLE_ROLES.contains(role)) { + result = jsonValue.toDouble(); + } else { + qCritical() << QString("Cant find data type of role %1!!!").arg(role); + } + return pair(role, result); +} diff --git a/formats/jsonparser.h b/formats/jsonparser.h index 36f557b..702414d 100644 --- a/formats/jsonparser.h +++ b/formats/jsonparser.h @@ -8,16 +8,22 @@ class QString; class QByteArray; class QJsonArray; +using namespace std; + class JsonParser { public: static QList> toItemValuesList(const QByteArray& jsonData, const QString& objectName = ""); + static QByteArray itemValuesListToJson(const QList>& itemValuesList, + const QString& objectName = ""); private: explicit JsonParser(); static QJsonArray extractItemArray(const QByteArray& jsonData, const QString& objectName); static QHash jsonObjectToItemValues(const QJsonObject& itemJsonObject); + + static pair getKeyValuePair(const QJsonObject& itemJsonObject, const int role); }; #endif // JSONPARSER_H diff --git a/genericcore.cpp b/genericcore.cpp index 3893b9c..86459f5 100644 --- a/genericcore.cpp +++ b/genericcore.cpp @@ -12,6 +12,7 @@ #include "CoreConfig.h" #include "constants.h" #include "data/filehandler.h" +#include "model/metadata.h" #include "model/tablemodel.h" #include @@ -98,7 +99,7 @@ void GenericCore::saveItems() { qDebug() << "saving items..."; const QJsonDocument doc = m_mainModel->getAllItemsAsJsonDoc(); - const bool successfulSave = FileHandler::saveToFile(doc, "items.json"); + const bool successfulSave = FileHandler::saveToFile(doc, ITEM_FILE_NAME); if (successfulSave) { // QStringList completedTaskStrings = m_model->completedTasks(); // appendCompletedTasksToFile(completedTaskStrings, "completed.txt"); @@ -122,7 +123,7 @@ void GenericCore::setupModels() { */ void GenericCore::initModelData() { qInfo() << "Trying to read model data from file..."; - const QByteArray jsonDoc = FileHandler::loadJSONDataFromFile("items.json"); + const QByteArray jsonDoc = FileHandler::loadJSONDataFromFile(ITEM_FILE_NAME); // qDebug() << "jsonDoc:" << jsonDoc; // TODO decide on lack of file(s) (config, data) if example items should be generated // (see welcome wizard) diff --git a/model/commands/edititemcommand.cpp b/model/commands/edititemcommand.cpp index 4c57bc1..449272b 100644 --- a/model/commands/edititemcommand.cpp +++ b/model/commands/edititemcommand.cpp @@ -2,6 +2,7 @@ #include +#include "../metadata.h" #include "../tablemodel.h" EditItemCommand::EditItemCommand(TableModel* model, @@ -19,24 +20,19 @@ EditItemCommand::EditItemCommand(TableModel* model, const int role = changedValues.firstKey(); const QVariant value = changedValues.first(); QString roleName = model->roleNames().value(role); - switch (role) { - case TableModel::NameRole: - case TableModel::DescriptionRole: - case TableModel::InfoRole: - case TableModel::AmountRole: - case TableModel::FactorRole: - commandText = QString("Setting '%1' of item '%2' to '%3'") - .arg(roleName) - .arg(index.data(TableModel::NameRole).toString()) - .arg(value.toString()); - break; - default: - commandText = QString("Edit item '%1'").arg(index.data(TableModel::NameRole).toString()); - break; + + if (USER_FACING_ROLES.contains(role)) { + commandText = QString("Setting '%1' of item '%2' to '%3'") + .arg(roleName) + .arg(index.data(DEFAULT_ROLE).toString()) + .arg(value.toString()); + } else { + qWarning() << "Role didn't match! Using a generic command text..."; + commandText = QString("Edit item '%1'").arg(index.data(DEFAULT_ROLE).toString()); } } else { qDebug() << "More than one value to change. Using a generic command text..."; - commandText = QString("Edit item '%1'").arg(index.data(TableModel::NameRole).toString()); + commandText = QString("Edit item '%1'").arg(index.data(DEFAULT_ROLE).toString()); } setText(commandText); diff --git a/model/commands/edititemcommand.h b/model/commands/edititemcommand.h index a8a2c23..f0ea909 100644 --- a/model/commands/edititemcommand.h +++ b/model/commands/edititemcommand.h @@ -8,6 +8,9 @@ class TableModel; class EditItemCommand : public QUndoCommand { public: + // TODO don't use simple pointer to model + /// Using simple pointer to model because there was a crash when closing the application with an + /// unclean undo stack EditItemCommand(TableModel* model, const QModelIndex& index, QMap& changedValues, diff --git a/model/commands/removerowscommand.cpp b/model/commands/removerowscommand.cpp index 306c9aa..487a916 100644 --- a/model/commands/removerowscommand.cpp +++ b/model/commands/removerowscommand.cpp @@ -20,13 +20,7 @@ RemoveRowsCommand::RemoveRowsCommand(TableModel* model, const int rowPosition = startRow + row; QModelIndex index = m_tableModel->index(rowPosition, 0); - // TODO use a (static) function "getRoleValueHash" or something - QHash values; - values[TableModel::NameRole] = m_tableModel->data(index, TableModel::NameRole); - values[TableModel::DescriptionRole] = m_tableModel->data(index, TableModel::DescriptionRole); - values[TableModel::InfoRole] = m_tableModel->data(index, TableModel::InfoRole); - values[TableModel::AmountRole] = m_tableModel->data(index, TableModel::AmountRole); - values[TableModel::FactorRole] = m_tableModel->data(index, TableModel::FactorRole); + QHash values = m_tableModel->getItemValues(index); m_valueList.append(values); } diff --git a/model/metadata.h b/model/metadata.h new file mode 100644 index 0000000..580d931 --- /dev/null +++ b/model/metadata.h @@ -0,0 +1,54 @@ +#ifndef METADATA_H +#define METADATA_H + +#include +#include +#include +#include + +/// model data +enum UserRoles { NameRole = Qt::UserRole + 1, DescriptionRole, InfoRole, AmountRole, FactorRole }; +static UserRoles DEFAULT_ROLE = NameRole; +static QList USER_FACING_ROLES = {NameRole, DescriptionRole, InfoRole, AmountRole, + FactorRole}; +static QHash ROLE_NAMES = {{NameRole, "Name"}, + {DescriptionRole, "Description"}, + {InfoRole, "Info"}, + {AmountRole, "Amount"}, + {FactorRole, "Factor"}}; +static QList STRING_ROLES = {NameRole, DescriptionRole, InfoRole}; +static QList INT_ROLES = {AmountRole}; +static QList DOUBLE_ROLES = {FactorRole}; + +/// JSON keys +static const QString ITEM_KEY_STRING = "items"; + +/// file naming +static const QString ITEM_FILE_NAME = ITEM_KEY_STRING + ".json"; + +/// functions +static int GET_ROLE_FOR_COLUMN(const int column) { + switch (column) { + case 0: + return NameRole; + break; + case 1: + return DescriptionRole; + break; + case 2: + return InfoRole; + break; + case 3: + return AmountRole; + break; + case 4: + return FactorRole; + break; + default: + qWarning() << QString("No role found for column %1! Returning 'NameRole'...").arg(column); + return NameRole; + break; + } +} + +#endif // METADATA_H diff --git a/model/modelitem.cpp b/model/modelitem.cpp index 5df55b6..7abf6bf 100644 --- a/model/modelitem.cpp +++ b/model/modelitem.cpp @@ -1,6 +1,6 @@ #include "modelitem.h" -#include "tablemodel.h" +#include "metadata.h" #include #include @@ -46,24 +46,22 @@ bool ModelItem::setItemData(const QMap& changedValues) { QJsonObject ModelItem::toJsonObject() const { QJsonObject itemObject; - // itemObject.insert("uuid", m_uuid.toString()); - // itemObject.insert("entryDateUTC", m_entryDateUTC.toString(Qt::ISODate)); - itemObject.insert(TableModel::ROLE_NAMES.value(TableModel::NameRole), - data(TableModel::NameRole).toString()); - itemObject.insert(TableModel::ROLE_NAMES.value(TableModel::DescriptionRole), - data(TableModel::DescriptionRole).toString()); - itemObject.insert(TableModel::ROLE_NAMES.value(TableModel::InfoRole), - data(TableModel::InfoRole).toString()); - itemObject.insert(TableModel::ROLE_NAMES.value(TableModel::AmountRole), - data(TableModel::AmountRole).toInt()); - itemObject.insert(TableModel::ROLE_NAMES.value(TableModel::FactorRole), - data(TableModel::FactorRole).toReal()); + // TODO add UUID and dates (entry, modification, end) - // if (m_modifiedDateUTC.isValid()) { - // itemObject.insert("modifiedDateUTC", m_modifiedDateUTC.toString(Qt::ISODate)); - // } - // if (m_endDateUTC.isValid()) { - // itemObject.insert("endDateUTC", m_endDateUTC.toString(Qt::ISODate)); - // } + QListIterator i(USER_FACING_ROLES); + while (i.hasNext()) { + const UserRoles role = i.next(); + const QString roleName = ROLE_NAMES.value(role); + const QVariant value = data(role); + if (STRING_ROLES.contains(role)) { + itemObject.insert(roleName, value.toString()); + } else if (INT_ROLES.contains(role)) { + itemObject.insert(roleName, value.toInt()); + } else if (DOUBLE_ROLES.contains(role)) { + itemObject.insert(roleName, value.toDouble()); + } else { + qCritical() << QString("Cant find data type of role %1!!!").arg(role); + } + } return itemObject; } diff --git a/model/tablemodel.cpp b/model/tablemodel.cpp index ce706fa..2ae6cdf 100644 --- a/model/tablemodel.cpp +++ b/model/tablemodel.cpp @@ -4,19 +4,13 @@ #include "commands/edititemcommand.h" #include "commands/insertrowscommand.h" #include "commands/removerowscommand.h" +#include "metadata.h" #include "modelitem.h" #include #include #include -QHash TableModel::ROLE_NAMES = {{NameRole, "Name"}, - {DescriptionRole, "Description"}, - {InfoRole, "Info"}, - {AmountRole, "Amount"}, - {FactorRole, "Factor"}}; -QList TableModel::intColumns = {"Amount", "Factor"}; - QByteArray TableModel::generateExampleItems() { QJsonDocument doc = QJsonDocument(); QJsonObject rootObject; @@ -26,18 +20,15 @@ QByteArray TableModel::generateExampleItems() { QJsonObject itemObject; // itemObject.insert("uuid", m_uuid.toString()); // itemObject.insert("entryDateUTC", m_entryDateUTC.toString(Qt::ISODate)); - itemObject.insert(TableModel::ROLE_NAMES.value(TableModel::NameRole), - QString("Item %1").arg(row)); - itemObject.insert(TableModel::ROLE_NAMES.value(TableModel::DescriptionRole), - QString("This is item %1").arg(row)); - itemObject.insert(TableModel::ROLE_NAMES.value(TableModel::InfoRole), - QString("Info of item %1").arg(row)); - itemObject.insert(TableModel::ROLE_NAMES.value(TableModel::AmountRole), row); - itemObject.insert(TableModel::ROLE_NAMES.value(TableModel::FactorRole), row * 1.1); + itemObject.insert(ROLE_NAMES.value(NameRole), QString("Item %1").arg(row)); + itemObject.insert(ROLE_NAMES.value(DescriptionRole), QString("This is item %1").arg(row)); + itemObject.insert(ROLE_NAMES.value(InfoRole), QString("Info of item %1").arg(row)); + itemObject.insert(ROLE_NAMES.value(AmountRole), row); + itemObject.insert(ROLE_NAMES.value(FactorRole), row * 1.1); array.append(itemObject); } - rootObject.insert("items", array); + rootObject.insert(ITEM_KEY_STRING, array); doc.setObject(rootObject); return doc.toJson(); @@ -74,7 +65,7 @@ QVariant TableModel::data(const QModelIndex& index, int role) const { return QVariant(); } - int roleForColumn = getRoleForColumn(column); + int roleForColumn = GET_ROLE_FOR_COLUMN(column); switch (role) { case Qt::DisplayRole: case Qt::EditRole: @@ -93,7 +84,7 @@ QVariant TableModel::data(const QModelIndex& index, int role) const { QVariant TableModel::headerData(int section, Qt::Orientation orientation, int role) const { if (role == Qt::DisplayRole) { if (orientation == Qt::Horizontal) { - const int columnRole = getRoleForColumn(section); + const int columnRole = GET_ROLE_FOR_COLUMN(section); const QString headerName = ROLE_NAMES.value(columnRole); return QString("%1").arg(headerName); } else { @@ -106,7 +97,7 @@ QVariant TableModel::headerData(int section, Qt::Orientation orientation, int ro bool TableModel::setData(const QModelIndex& index, const QVariant& value, int role) { if (role == Qt::EditRole && checkIndex(index)) { const int column = index.column(); - const int roleForColumn = getRoleForColumn(column); + const int roleForColumn = GET_ROLE_FOR_COLUMN(column); return setItemData(index, {{roleForColumn, value}}); } return false; @@ -129,6 +120,17 @@ bool TableModel::setItemData(const QModelIndex& index, const QMap return false; } +QHash TableModel::getItemValues(const QModelIndex& index) const { + QHash values; + + QListIterator i(USER_FACING_ROLES); + while (i.hasNext()) { + const UserRoles role = i.next(); + values.insert(role, data(index, role)); + } + return values; +} + QJsonDocument TableModel::getAllItemsAsJsonDoc() const { QJsonDocument doc = QJsonDocument(); QJsonObject rootObject; @@ -138,7 +140,7 @@ QJsonDocument TableModel::getAllItemsAsJsonDoc() const { QJsonObject itemObject = item->toJsonObject(); array.append(itemObject); } - rootObject.insert("items", array); + rootObject.insert(ITEM_KEY_STRING, array); doc.setObject(rootObject); return doc; @@ -176,7 +178,7 @@ void TableModel::insertItems(int startPosition, startPosition = m_items.size(); } - QList> valueList = JsonParser::toItemValuesList(jsonDoc, "items"); + QList> valueList = JsonParser::toItemValuesList(jsonDoc, ITEM_KEY_STRING); InsertRowsCommand* insertCommand = new InsertRowsCommand(this, startPosition, valueList); m_undoStack->push(insertCommand); @@ -219,29 +221,6 @@ void TableModel::execEditItemData(const int row, const QMap& chan } } -int TableModel::getRoleForColumn(const int column) const { - switch (column) { - case 0: - return NameRole; - break; - case 1: - return DescriptionRole; - break; - case 2: - return InfoRole; - break; - case 3: - return AmountRole; - break; - case 4: - return FactorRole; - break; - default: - return NameRole; - break; - } -} - QMap TableModel::onlyChangedValues(const QModelIndex& index, const QMap& roleValueMap) const { QMap result; @@ -268,9 +247,11 @@ QMap TableModel::onlyChangedValues(const QModelIndex& index, } bool TableModel::isEmptyValueEqualToZero(const int role) const { - const QString roleName = ROLE_NAMES.value(role); - if (intColumns.contains(roleName)) { + if (INT_ROLES.contains(role)) { return true; + } else if (DOUBLE_ROLES.contains(role)) { + return true; + } else { + return false; } - return false; } diff --git a/model/tablemodel.h b/model/tablemodel.h index 2bb758a..c7650f2 100644 --- a/model/tablemodel.h +++ b/model/tablemodel.h @@ -16,9 +16,6 @@ class TableModel : public QAbstractTableModel { friend class EditItemCommand; public: - enum UserRoles { NameRole = Qt::UserRole + 1, DescriptionRole, InfoRole, AmountRole, FactorRole }; - static QHash ROLE_NAMES; - static QList intColumns; static QByteArray generateExampleItems(); explicit TableModel(QUndoStack* undoStack, QObject* parent = nullptr); @@ -35,6 +32,7 @@ class TableModel : public QAbstractTableModel { bool setData(const QModelIndex& index, const QVariant& value, int role) override; bool setItemData(const QModelIndex& index, const QMap& roles) override; + QHash getItemValues(const QModelIndex& index) const; QJsonDocument getAllItemsAsJsonDoc() const; public slots: @@ -59,7 +57,6 @@ class TableModel : public QAbstractTableModel { void execEditItemData(const int row, const QMap& changedValues); /// misc functions - int getRoleForColumn(const int column) const; QMap onlyChangedValues(const QModelIndex& index, const QMap& roleValueMap) const; bool isEmptyValueEqualToZero(const int role) const;