diff --git a/RecipeDB.pro b/RecipeDB.pro index 2c957a5..0071690 100644 --- a/RecipeDB.pro +++ b/RecipeDB.pro @@ -25,7 +25,9 @@ SOURCES += model/recipe/instruction.cpp \ SQLite/sqlite3.c \ model/database/resulttable.cpp \ model/database/recipedatabase.cpp \ - utils/fileutils.cpp + utils/fileutils.cpp \ + gui/newrecipedialog.cpp \ + model/recipe/tags/taglistmodel.cpp HEADERS += model/recipe/instruction.h \ model/recipe/recipe.h \ @@ -40,11 +42,14 @@ HEADERS += model/recipe/instruction.h \ SQLite/sqlite3ext.h \ model/database/resulttable.h \ model/database/recipedatabase.h \ - utils/fileutils.h + utils/fileutils.h \ + gui/newrecipedialog.h \ + model/recipe/tags/taglistmodel.h LIBS += -ldl \ -FORMS += gui/mainwindow.ui +FORMS += gui/mainwindow.ui \ + gui/newrecipedialog.ui DISTFILES += \ .gitignore diff --git a/gui/mainwindow.ui b/gui/mainwindow.ui index 1db1cc9..ae24fae 100644 --- a/gui/mainwindow.ui +++ b/gui/mainwindow.ui @@ -109,12 +109,25 @@ - Source Sans Pro Light + Noto Sans CJK KR Light 20 + PreferAntialias + + false + - background-color: rgb(83, 75, 255); + QPushButton#newButton { + background-color: rgb(235, 235, 255); + border: 0px; +} +QPushButton#newButton:hover{ + background-color: rgb(245, 245, 255); +} +QPushButton#newButton:pressed{ + background-color: rgb(255, 255, 255); +} New @@ -134,12 +147,22 @@ - Source Sans Pro Light + Noto Sans CJK KR Light 20 + PreferAntialias - background-color: rgb(112, 105, 255); + QPushButton#openButton { + background-color: rgb(222, 226, 255); + border: 0px; +} +QPushButton#openButton:hover{ + background-color: rgb(232, 236, 255); +} +QPushButton#openButton:pressed{ + background-color: rgb(255, 255, 255); +} Open @@ -156,16 +179,32 @@ - Source Sans Pro Light + Noto Sans CJK KR Light 20 + PreferAntialias + + false + - background-color: rgb(137, 131, 255); + QPushButton#browseButton { + background-color: rgb(215, 215, 255); + border: 0px; +} +QPushButton#browseButton:hover{ + background-color: rgb(225, 225, 255); +} +QPushButton#browseButton:pressed{ + background-color: rgb(255, 255, 255); +} Browse + + false + @@ -214,8 +253,13 @@ - Source Sans Pro Light + Noto Sans CJK KR Light 24 + 50 + false + false + PreferAntialias + true @@ -299,7 +343,7 @@ - Source Sans Pro Light + Noto Sans CJK KR Thin 18 @@ -324,8 +368,9 @@ - Source Sans Pro Light - 16 + Noto Sans CJK KR Light + 12 + PreferAntialias @@ -343,6 +388,9 @@ Qt::ScrollBarAlwaysOff + + QAbstractItemView::NoSelection + true @@ -397,7 +445,7 @@ - Source Sans Pro Light + Noto Sans CJK KR Thin 18 @@ -414,6 +462,12 @@ + + + Noto Sans CJK KR Medium + PreferAntialias + + background-color: rgb(244, 244, 244); @@ -445,11 +499,11 @@ <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } -</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;"> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" color:#00ff40;">This is some </span><span style=" font-size:16pt; color:#a33c3e;">colored text and </span><a href="https://www.google.com"><span style=" font-size:8pt; text-decoration: underline; color:#0000ff;">link</span></a></p></body></html> +</style></head><body style=" font-family:'Noto Sans CJK KR Medium'; font-size:11pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'MS Shell Dlg 2'; font-size:8.25pt; color:#00ff40;">This is some </span><span style=" font-family:'MS Shell Dlg 2'; font-size:16pt; color:#a33c3e;">colored text and </span><a href="https://www.google.com"><span style=" font-family:'MS Shell Dlg 2'; font-size:8pt; text-decoration: underline; color:#0000ff;">link</span></a></p></body></html> - Qt::LinksAccessibleByMouse + Qt::TextSelectableByMouse diff --git a/gui/newrecipedialog.cpp b/gui/newrecipedialog.cpp new file mode 100644 index 0000000..7405d09 --- /dev/null +++ b/gui/newrecipedialog.cpp @@ -0,0 +1,103 @@ +#include "newrecipedialog.h" +#include "ui_newrecipedialog.h" + +NewRecipeDialog::NewRecipeDialog(QWidget *parent) : + QDialog(parent), + ui(new Ui::NewRecipeDialog){ + ui->setupUi(this); + + setModal(true); + + ui->ingredientsListView->setModel(&this->ingredientListModel); + ui->tagsListView->setModel(&this->tagsListModel); +} + +NewRecipeDialog::NewRecipeDialog(RecipeDatabase *db, QWidget *parent) : NewRecipeDialog(parent){ + this->recipeDB = db; + + + this->populateIngredientsBox(); + this->populateUnitsBox(); + this->populateTagsBox(); +} + +NewRecipeDialog::~NewRecipeDialog(){ + delete ui; +} + +Recipe NewRecipeDialog::getRecipe(){ + Recipe r(ui->recipeNameEdit->text().toStdString(), + this->ingredientListModel.getIngredients(), + ui->instructionsTextEdit->toHtml().toStdString(), + QImage(),//Image + this->tagsListModel.getTags(),//Tags + QDate::currentDate(), + ui->prepTimeEdit->time(), + ui->cookTimeEdit->time(), + (float)ui->servingsSpinBox->value()); +} + +bool NewRecipeDialog::isAccepted() const{ + return this->accepted; +} + +void NewRecipeDialog::populateIngredientsBox(){ + this->ingredients = this->recipeDB->retrieveAllIngredients(); + ui->ingredientNameBox->clear(); + for (unsigned int i = 0; i < this->ingredients.size(); i++){ + QString s = QString::fromStdString(this->ingredients[i].getName()); + ui->ingredientNameBox->insertItem(i, s); + } +} + +void NewRecipeDialog::populateUnitsBox(){ + this->units = this->recipeDB->retrieveAllUnitsOfMeasure(); + ui->unitComboBox->clear(); + for (unsigned int i = 0; i < this->units.size(); i++){ + QString s = QString::fromStdString(this->units[i].getName()); + ui->unitComboBox->insertItem(i, s); + } +} + +void NewRecipeDialog::populateTagsBox(){ + this->tags = this->recipeDB->retrieveAllTags(); + ui->tagsComboBox->clear(); + for (unsigned int i = 0; i < this->tags.size(); i++){ + QString s = QString::fromStdString(this->tags[i].getValue()); + ui->tagsComboBox->insertItem(i, s); + } +} + +void NewRecipeDialog::on_addIngredientButton_clicked(){ + //Construct a recipe ingredient from the supplied data. + Ingredient i = this->ingredients[ui->ingredientNameBox->currentIndex()]; + UnitOfMeasure u = this->units[ui->unitComboBox->currentIndex()]; + RecipeIngredient ri(i, ui->quantitySpinBox->value(), u, ui->commentsLineEdit->text().toStdString()); + this->ingredientListModel.addIngredient(ri); +} + +void NewRecipeDialog::on_italicsButton_clicked(){ + ui->instructionsTextEdit->setFontItalic(ui->italicsButton->isChecked()); +} + +void NewRecipeDialog::on_boldButton_clicked(){ + if (ui->boldButton->isChecked()){ + ui->instructionsTextEdit->setFontWeight(QFont::Bold); + } else { + ui->instructionsTextEdit->setFontWeight(QFont::Normal); + } +} + +void NewRecipeDialog::on_buttonBox_accepted(){ + this->accepted = true; + this->close(); +} + +void NewRecipeDialog::on_buttonBox_rejected(){ + this->close(); +} + +void NewRecipeDialog::on_addTagButton_clicked(){ + //Add a tag to the list of those prepared to be added. + this->tagsListModel.addTag(this->tags[ui->tagsComboBox->currentIndex()]); +} diff --git a/gui/newrecipedialog.h b/gui/newrecipedialog.h new file mode 100644 index 0000000..47cd848 --- /dev/null +++ b/gui/newrecipedialog.h @@ -0,0 +1,55 @@ +#ifndef NEWRECIPEDIALOG_H +#define NEWRECIPEDIALOG_H + +#include +#include + +#include "model/database/recipedatabase.h" +#include "model/recipe/ingredients/ingredientlistmodel.h" +#include "model/recipe/tags/taglistmodel.h" + +namespace Ui { +class NewRecipeDialog; +} + +class NewRecipeDialog : public QDialog +{ + Q_OBJECT + + public: + explicit NewRecipeDialog(QWidget *parent = 0); + NewRecipeDialog(RecipeDatabase *db, QWidget *parent = 0); + ~NewRecipeDialog(); + + Recipe getRecipe(); + bool isAccepted() const; + private slots: + void on_addIngredientButton_clicked(); + + void on_italicsButton_clicked(); + + void on_boldButton_clicked(); + + void on_buttonBox_accepted(); + + void on_buttonBox_rejected(); + + void on_addTagButton_clicked(); + + private: + Ui::NewRecipeDialog *ui; + RecipeDatabase *recipeDB; + vector ingredients; + vector units; + vector tags; + IngredientListModel ingredientListModel; + TagListModel tagsListModel; + bool accepted = false; + + //Helper functions to fill fields. + void populateIngredientsBox(); + void populateUnitsBox(); + void populateTagsBox(); +}; + +#endif // NEWRECIPEDIALOG_H diff --git a/gui/newrecipedialog.ui b/gui/newrecipedialog.ui new file mode 100644 index 0000000..78e7f57 --- /dev/null +++ b/gui/newrecipedialog.ui @@ -0,0 +1,685 @@ + + + NewRecipeDialog + + + true + + + + 0 + 0 + 640 + 689 + + + + + 0 + 0 + + + + New Recipe + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Qt::LeftToRight + + + background-color: rgb(234, 235, 255); + + + + + + + + + + Noto Sans CJK KR + 14 + 50 + false + false + + + + Edit Recipe + + + Qt::AlignCenter + + + + + + + + PreferAntialias + + + + Qt::AlignCenter + + + Recipe Name + + + + + + + + + + + + + + + + Prep Time + + + Qt::AlignCenter + + + + + + + + 0 + 0 + 0 + 1999 + 12 + 27 + + + + QDateTimeEdit::HourSection + + + hh:mm:ss + + + Qt::UTC + + + + + + + + + + + + + + + + Cook Time + + + Qt::AlignCenter + + + + + + + QDateTimeEdit::HourSection + + + hh:mm:ss + + + Qt::UTC + + + + + + + + + + + + + + + + Servings + + + Qt::AlignCenter + + + + + + + 1 + + + 1.000000000000000 + + + + + + + + + + + + + + + + background-color: rgb(222, 226, 255); + + + + + + Tags + + + Qt::AlignCenter + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + New + + + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Add + + + + + + + Delete + + + + + + + + + + false + + + background-color: rgb(255, 255, 255); + + + QFrame::NoFrame + + + + + + + + + + + + + + 0 + 0 + + + + background-color: rgb(245, 245, 255); + + + + 0 + + + QLayout::SetMaximumSize + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Ingredients + + + Qt::AlignCenter + + + + + + + + + + background-color: rgb(255, 255, 255); + + + QFrame::NoFrame + + + 100 + + + + + + + + 5 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Add Ingredient + + + Qt::AlignCenter + + + + + + + + 2 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + + + + + + + + New + + + + + + + + + + + 0 + 36 + + + + + 2 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + Noto Sans CJK KR + 14 + 50 + false + false + + + + Amount + + + + + + + + 0 + 0 + + + + 10000.000000000000000 + + + 1.000000000000000 + + + + + + + + + + New + + + + + + + + + + + 0 + 0 + + + + false + + + Qt::AlignCenter + + + Comments + + + false + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Add + + + + + + + Delete + + + + + + + + + + + + + + + + + + + + + + Instructions + + + Qt::AlignCenter + + + + + + + + + + + Liberation Serif + 12 + true + + + + I + + + true + + + Qt::ToolButtonIconOnly + + + + + + + + Liberation Serif + 12 + 75 + true + + + + B + + + true + + + false + + + false + + + + + + + + + + Enter instructions here. + + + + + + + + + + Qt::LeftToRight + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + + + diff --git a/main.cpp b/main.cpp index d67a414..23f8db2 100644 --- a/main.cpp +++ b/main.cpp @@ -1,4 +1,5 @@ #include "userInterface/mainwindow.h" +#include "gui/newrecipedialog.h" #include #include "model/database/database.h" @@ -13,27 +14,42 @@ int main(int argc, char *argv[]) //TESTING CODE RecipeDatabase recipeDB("recipes"); -// recipeDB.storeIngredient(Ingredient("Apple", "Fruit")); -// recipeDB.storeIngredient(Ingredient("Corn", "Vegetable")); -// recipeDB.storeIngredient(Ingredient("Lettuce", "Vegetable")); -// recipeDB.storeIngredient(Ingredient("Carrot", "Vegetable")); - -// recipeDB.executeSQL("SELECT * FROM ingredient;").printData(); //TESTING CODE vector ri; - ri.push_back(RecipeIngredient("flour", "grains", 3.0f, UnitOfMeasure("cup", "cups", "c"))); - ri.push_back(RecipeIngredient("Baking Powder", "Additives", 1.0f, UnitOfMeasure("Teaspoon", "Teaspoons", "Tsp"))); + ri.push_back(RecipeIngredient("flour", "grains", 3.0f, UnitOfMeasure("cup", "cups", "c", UnitOfMeasure::VOLUME, 1.0), "")); + ri.push_back(RecipeIngredient("baking powder", "additives", 1.0f, UnitOfMeasure("teaspoon", "teaspoons", "tsp", UnitOfMeasure::VOLUME, 1.0), "")); - Recipe rec("Example", ri, Instruction("BOLDiTaLiCs"), QImage(), vector(), QDate::currentDate(), QTime(0, 30), QTime(0, 25), 10.0f); + Recipe rec("Example", + ri, + Instruction("BOLDiTaLiCs"), + QImage(), + vector({RecipeTag("testing"), + RecipeTag("fake")}), + QDate::currentDate(), + QTime(0, 30), + QTime(0, 25), + 10.0f); bool success = recipeDB.storeRecipe(rec); printf("Storage successful: %d\n", success); + recipeDB.storeUnitOfMeasure(UnitOfMeasure("tablespoon", "tablespoons", "tbsp", UnitOfMeasure::VOLUME, 1.0)); + recipeDB.storeUnitOfMeasure(UnitOfMeasure("pinch", "pinches", "pch", UnitOfMeasure::VOLUME, 1.0)); + recipeDB.storeUnitOfMeasure(UnitOfMeasure("gram", "grams", "g", UnitOfMeasure::MASS, 1.0)); + Recipe reloadRec = recipeDB.retrieveRecipe("Example"); reloadRec.print(); w.loadFromRecipe(reloadRec); + NewRecipeDialog d(&recipeDB); + d.show(); + d.exec(); + + if (d.isAccepted()){ + printf("Accepted the dialog.\n"); + } + return a.exec(); } diff --git a/model/database/database.cpp b/model/database/database.cpp index 6fb4394..636a137 100644 --- a/model/database/database.cpp +++ b/model/database/database.cpp @@ -42,7 +42,7 @@ ResultTable Database::selectFrom(string tableName, string columnNames, string co if (columnNames.size() == 0 || tableName.empty()){ return ResultTable(); } - string query = "SELECT " + columnNames + " FROM " + tableName + " WHERE " + conditions + ";"; + string query = "SELECT " + columnNames + " FROM " + tableName + " " + conditions + ";"; return this->executeSQL(query); } @@ -81,8 +81,7 @@ bool Database::tableExists(string tableName){ if (tableName.empty() || this->db == NULL || !this->dbIsOpen){ return false; } - ResultTable t = this->selectFrom("sqlite_master", "name", "type='table' AND name='"+tableName+"'"); - //ResultTable t = executeSQL("SELECT name FROM sqlite_master WHERE type='table' AND name='"+tableName+"';"); + ResultTable t = this->selectFrom("sqlite_master", "name", "WHERE type='table' AND name='"+tableName+"'"); return !t.isEmpty(); } diff --git a/model/database/recipedatabase.cpp b/model/database/recipedatabase.cpp index 4c991da..b64a660 100644 --- a/model/database/recipedatabase.cpp +++ b/model/database/recipedatabase.cpp @@ -5,9 +5,9 @@ RecipeDatabase::RecipeDatabase(string filename) : Database(filename){ } bool RecipeDatabase::storeRecipe(Recipe recipe){ - ///TODO: Implement this in a smart way using transaction. + //Store a recipe, if it doesn't already exist. This first tries to create the recipe entry, then all subsequent supporting table entries. this->executeSQL("BEGIN;"); - ResultTable t = this->selectFrom("recipe", "*", "name="+surroundString(recipe.getName(), "'")); + ResultTable t = this->selectFrom("recipe", "*", "WHERE name="+surroundString(recipe.getName(), "'")); if (!t.isEmpty()){ fprintf(stderr, "Error storing recipe: Recipe with name %s already exists.\n", recipe.getName().c_str()); } else { @@ -50,17 +50,11 @@ bool RecipeDatabase::storeRecipe(Recipe recipe){ } bool RecipeDatabase::storeRecipeIngredient(RecipeIngredient ri, int recipeId){ - //First check if the base ingredient has been added to the database. This is done within storeIngredient(). - ResultTable t = this->selectFrom("ingredient", "ingredientId", "name="+surroundString(ri.getName(), "'")); - int ingId = 0; - if (t.isEmpty()){ - if (!this->insertInto("ingredient", vector({"foodGroup", "name"}), vector({ri.getFoodGroup(), ri.getName()}))){ - return false; - } - ingId = this->getLastInsertedRowId(); - } else { - ingId = std::stoi(t.valueAt(0, 0)); - } + int ingId = this->storeIngredient(ri); + if (ingId < 0) return false; + + if (!this->storeUnitOfMeasure(ri.getUnit())) return false; + return this->insertInto("recipeIngredient", vector({ "ingredientId", @@ -78,13 +72,43 @@ bool RecipeDatabase::storeRecipeIngredient(RecipeIngredient ri, int recipeId){ })); } -void RecipeDatabase::storeIngredient(Ingredient ingredient){ - ResultTable t = this->selectFrom("ingredient", "*", "name="+surroundString(ingredient.getName(), "'")); +int RecipeDatabase::storeIngredient(Ingredient ingredient){ + ResultTable t = this->selectFrom("ingredient", "*", "WHERE name="+surroundString(ingredient.getName(), "'")); if (t.isEmpty()){ - this->insertInto("ingredient", vector({"foodGroup", "name"}), vector({ingredient.getFoodGroup(), ingredient.getName()})); + bool success = this->insertInto("ingredient", vector({"foodGroup", "name"}), vector({ingredient.getFoodGroup(), ingredient.getName()})); + if (success){ + return this->getLastInsertedRowId(); + } else { + return -1; + } + } else { + return std::stoi(t.valueAt(0, 0)); } } +bool RecipeDatabase::storeUnitOfMeasure(UnitOfMeasure u){ + ResultTable t = this->selectFrom("unitOfMeasure", "name", "WHERE name="+surroundString(u.getName(), "'")); + if (!t.isEmpty()){ + return true; + } + bool success = this->insertInto("unitOfMeasure", + vector({ + "name", + "plural", + "abbreviation", + "type", + "metricCoefficient" + }), + vector({ + u.getName(), + u.getNamePlural(), + u.getAbbreviation(), + std::to_string(u.getType()), + std::to_string(u.getMetricCoefficient()) + })); + return success; +} + bool RecipeDatabase::storeInstruction(Instruction instruction, int recipeId){ return FileUtils::saveInstruction(recipeId, instruction); } @@ -112,12 +136,11 @@ bool RecipeDatabase::storeTags(vector tags, int recipeId){ } Recipe RecipeDatabase::retrieveRecipe(string name){ - ResultTable t = this->selectFrom("recipe", "*", "name="+surroundString(name, "'")); + ResultTable t = this->selectFrom("recipe", "*", "WHERE name="+surroundString(name, "'")); if (t.isEmpty()){ fprintf(stderr, "Error: No recipe with name %s found!\n", name.c_str()); return Recipe(); } - t.printData(); Recipe r; int id = std::stoi(t.valueAt(0, 0)); r.setName(t.valueAt(0, 1)); @@ -128,24 +151,78 @@ Recipe RecipeDatabase::retrieveRecipe(string name){ r.setInstruction(FileUtils::loadInstruction(id)); r.setImage(FileUtils::loadImage(id)); r.setIngredients(this->retrieveRecipeIngredients(id)); + r.setTags(this->retrieveTags(id)); return r; } vector RecipeDatabase::retrieveRecipeIngredients(int recipeId){ - ResultTable t = this->executeSQL("SELECT ingredient.name, ingredient.foodGroup, recipeIngredient.quantity, recipeIngredient.unitName, recipeIngredient.comment " + ResultTable t = this->executeSQL("SELECT ingredient.name, ingredient.foodGroup, "//0, 1 + "recipeIngredient.quantity, recipeIngredient.unitName, recipeIngredient.comment,"//2, 3, 4 + "unitOfMeasure.name, unitOfMeasure.plural, unitOfMeasure.abbreviation, unitOfMeasure.type, unitOfMeasure.metricCoefficient "//5, 6, 7, 8, 9 "FROM ingredient " "INNER JOIN recipeIngredient " "ON ingredient.ingredientId = recipeIngredient.ingredientId " - "AND recipeIngredient.recipeId = "+std::to_string(recipeId)+";"); - t.printData(); + "INNER JOIN unitOfMeasure " + "ON recipeIngredient.unitName = unitOfMeasure.name " + "WHERE recipeIngredient.recipeId = "+std::to_string(recipeId)+";"); vector ings; for (unsigned int row = 0; row < t.rowCount(); row++){ - RecipeIngredient r(t.valueAt(row, 0), t.valueAt(row, 1), std::stof(t.valueAt(row, 2)), UnitOfMeasure(t.valueAt(row, 3))); + RecipeIngredient r(t.valueAt(row, 0), + t.valueAt(row, 1), + std::stof(t.valueAt(row, 2)), + UnitOfMeasure(t.valueAt(row, 5), t.valueAt(row, 6), t.valueAt(row, 7), std::stoi(t.valueAt(row, 8)), std::stod(t.valueAt(row, 9))), + t.valueAt(row, 4)); ings.push_back(r); } return ings; } +vector RecipeDatabase::retrieveAllIngredients(){ + ResultTable t = this->selectFrom("ingredient", "*", "ORDER BY name"); + vector ings; + for (unsigned int row = 0; row < t.rowCount(); row++){ + Ingredient i(t.valueAt(row, 2), t.valueAt(row, 1)); + ings.push_back(i); + } + return ings; +} + +vector RecipeDatabase::retrieveAllUnitsOfMeasure(){ + ResultTable t = this->selectFrom("unitOfMeasure", "*", "ORDER BY name"); + vector units; + if (!t.isEmpty()){ + for (unsigned int row = 0; row < t.rowCount(); row++){ + UnitOfMeasure u(t.valueAt(row, 0), t.valueAt(row, 1), t.valueAt(row, 2), std::stoi(t.valueAt(row, 3)), std::stod(t.valueAt(row, 4))); + units.push_back(u); + } + } + return units; +} + +vector RecipeDatabase::retrieveTags(int recipeId){ + ResultTable t = this->selectFrom("recipeTag", "tagName", "WHERE recipeId="+std::to_string(recipeId)); + vector tags; + if (!t.isEmpty()){ + for (unsigned int row = 0; row < t.rowCount(); row++){ + RecipeTag tag(t.valueAt(row, 0)); + tags.push_back(tag); + } + } + return tags; +} + +vector RecipeDatabase::retrieveAllTags(){ + ResultTable t = this->selectFrom("recipeTag", "tagName", "ORDER BY tagName"); + vector tags; + if (!t.isEmpty()){ + for (unsigned int row = 0; row < t.rowCount(); row++){ + RecipeTag tag(t.valueAt(row, 0)); + tags.push_back(tag); + } + } + return tags; +} + void RecipeDatabase::ensureTablesExist(){ //Make sure that foreign keys are enabled. this->executeSQL("PRAGMA foreign_keys = ON;"); @@ -156,6 +233,13 @@ void RecipeDatabase::ensureTablesExist(){ "ingredientId INTEGER PRIMARY KEY," "foodGroup varchar," "name varchar UNIQUE);"); + //Unit of Measure table. + this->executeSQL("CREATE TABLE IF NOT EXISTS unitOfMeasure(" + "name varchar UNIQUE PRIMARY KEY," + "plural varchar," + "abbreviation varchar," + "type int," + "metricCoefficient real);"); //Recipe table. Each recipe can have at most one instruction, and one image. this->executeSQL("CREATE TABLE IF NOT EXISTS recipe(" "recipeId INTEGER PRIMARY KEY," @@ -177,6 +261,7 @@ void RecipeDatabase::ensureTablesExist(){ "unitName varchar," "comment varchar," "FOREIGN KEY (ingredientId) REFERENCES ingredient(ingredientId)," - "FOREIGN KEY (recipeId) REFERENCES recipe(recipeId));"); + "FOREIGN KEY (recipeId) REFERENCES recipe(recipeId)," + "FOREIGN KEY (unitName) REFERENCES unitOfMeasure(name));"); this->executeSQL("COMMIT;"); } diff --git a/model/database/recipedatabase.h b/model/database/recipedatabase.h index e91b45d..f1d9867 100644 --- a/model/database/recipedatabase.h +++ b/model/database/recipedatabase.h @@ -22,14 +22,21 @@ class RecipeDatabase : public Database bool storeRecipe(Recipe recipe); //SQL Helper methods. + //Storage. bool storeRecipeIngredient(RecipeIngredient ri, int recipeId); - void storeIngredient(Ingredient ingredient); + int storeIngredient(Ingredient ingredient); + bool storeUnitOfMeasure(UnitOfMeasure u); bool storeInstruction(Instruction instruction, int recipeId); bool storeImage(QImage image, int recipeId); bool storeTags(vector tags, int recipeId); + //Retrieval. Recipe retrieveRecipe(string name); vector retrieveRecipeIngredients(int recipeId); + vector retrieveAllIngredients(); + vector retrieveAllUnitsOfMeasure(); + vector retrieveTags(int recipeId); + vector retrieveAllTags(); private: //Utility methods. diff --git a/model/recipe/ingredients/ingredientlistmodel.cpp b/model/recipe/ingredients/ingredientlistmodel.cpp index de88da5..1370bc7 100644 --- a/model/recipe/ingredients/ingredientlistmodel.cpp +++ b/model/recipe/ingredients/ingredientlistmodel.cpp @@ -10,10 +10,23 @@ int IngredientListModel::rowCount(const QModelIndex &parent) const{ QVariant IngredientListModel::data(const QModelIndex &index, int role) const{ int row = index.row(); + RecipeIngredient i = this->ingredients[row]; + + string displayStr; + + if (std::ceil(i.getQuantity()) == i.getQuantity()){ + //The quantity is an integer and should be casted. + displayStr += std::to_string((int)i.getQuantity()); + } else { + float q = i.getQuantity(); + displayStr += toString(q); + } + + displayStr += " " + i.getUnit().getAbbreviation() + " " + i.getName(); switch(role){ case Qt::DisplayRole: - return QString::fromStdString(ingredients[row].getName()); + return QString::fromStdString(displayStr); } return QVariant(); @@ -23,5 +36,37 @@ void IngredientListModel::setIngredients(vector ingredients){ this->ingredients = ingredients; QModelIndex index = createIndex(0, 0); QModelIndex bottomIndex = createIndex(ingredients.size()-1, 0); - emit dataChanged(index, bottomIndex); + emit dataChanged(index, bottomIndex); +} + +bool IngredientListModel::addIngredient(RecipeIngredient ri){ + //Add only if it doesn't exist already. + for (unsigned int i = 0; i < this->ingredients.size(); i++){ + if (!this->ingredients[i].getName().compare(ri.getName())){ + return false; + } + } + this->ingredients.push_back(ri); + QModelIndex index = createIndex(this->ingredients.size()-1, 0); + QModelIndex bottomIndex = createIndex(this->ingredients.size()-1, 0); + emit dataChanged(index, bottomIndex); + return true; +} + +vector IngredientListModel::getIngredients(){ + return this->ingredients; +} + +string toString(float val){ + float decimal = std::fmod(val, 1.0f); + int places = 1; + while (std::fmod(decimal * 10, 1.0f) > 0){ + decimal *= 10; + places++; + } + char buffer[50]; + string arg = "%."+std::to_string(places)+"f"; + sprintf(buffer, arg.c_str(), val); + string s = buffer; + return s; } diff --git a/model/recipe/ingredients/ingredientlistmodel.h b/model/recipe/ingredients/ingredientlistmodel.h index 64ca72a..1f3605c 100644 --- a/model/recipe/ingredients/ingredientlistmodel.h +++ b/model/recipe/ingredients/ingredientlistmodel.h @@ -17,9 +17,16 @@ public: //Custom methods to handle ingredient data. void setIngredients(vector ingredients); + bool addIngredient(RecipeIngredient ri); + vector getIngredients(); private: vector ingredients; + + //Helper for printing. + }; +string toString(float val); + #endif // INGREDIENTLISTMODEL_H diff --git a/model/recipe/ingredients/recipeingredient.cpp b/model/recipe/ingredients/recipeingredient.cpp index c48155c..7a3b75a 100644 --- a/model/recipe/ingredients/recipeingredient.cpp +++ b/model/recipe/ingredients/recipeingredient.cpp @@ -1,15 +1,17 @@ #include "model/recipe/ingredients/recipeingredient.h" -RecipeIngredient::RecipeIngredient(string name, string foodGroup, float quantity, UnitOfMeasure unit) : Ingredient(name, foodGroup){ +RecipeIngredient::RecipeIngredient(string name, string foodGroup, float quantity, UnitOfMeasure unit, string comment) : Ingredient(name, foodGroup){ setQuantity(quantity); setUnit(unit); + setComment(comment); } -RecipeIngredient::RecipeIngredient(Ingredient i, float quantity, UnitOfMeasure unit){ +RecipeIngredient::RecipeIngredient(Ingredient i, float quantity, UnitOfMeasure unit, string comment){ setName(i.getName()); setFoodGroup(i.getFoodGroup()); setQuantity(quantity); setUnit(unit); + setComment(comment); } RecipeIngredient::RecipeIngredient(){ diff --git a/model/recipe/ingredients/recipeingredient.h b/model/recipe/ingredients/recipeingredient.h index 70c20f6..ae5fef9 100644 --- a/model/recipe/ingredients/recipeingredient.h +++ b/model/recipe/ingredients/recipeingredient.h @@ -16,9 +16,9 @@ class RecipeIngredient : public Ingredient { public: //Constructor for new RecipeIngredient without starting child ingredient. - RecipeIngredient(string name, string foodGroup, float quantity, UnitOfMeasure unit); + RecipeIngredient(string name, string foodGroup, float quantity, UnitOfMeasure unit, string comment); //Constructor using data from a child ingredient. - RecipeIngredient(Ingredient i, float quantity, UnitOfMeasure unit); + RecipeIngredient(Ingredient i, float quantity, UnitOfMeasure unit, string comment); RecipeIngredient(); //Getters diff --git a/model/recipe/ingredients/unitofmeasure.cpp b/model/recipe/ingredients/unitofmeasure.cpp index ddf62c8..dedcb75 100644 --- a/model/recipe/ingredients/unitofmeasure.cpp +++ b/model/recipe/ingredients/unitofmeasure.cpp @@ -1,19 +1,23 @@ #include "unitofmeasure.h" -UnitOfMeasure::UnitOfMeasure(string name, string plural, string abbreviation){ +UnitOfMeasure::UnitOfMeasure(string name, string plural, string abbreviation, int type, double coef){ this->name = name; this->plural = plural; this->abbreviation = abbreviation; + this->type = type; + this->metricCoefficient = coef; } UnitOfMeasure::UnitOfMeasure(string name){ this->name = name; this->plural = name + "s"; this->abbreviation = "NULL"; + this->type = MISC; + this->metricCoefficient = 1; ///TODO: Make actual guessing of this stuff. } -UnitOfMeasure::UnitOfMeasure() : UnitOfMeasure::UnitOfMeasure("", "", ""){ +UnitOfMeasure::UnitOfMeasure() : UnitOfMeasure::UnitOfMeasure("", "", "", MISC, 1.0){ //Default constructor initializes all fields to empty strings. } @@ -26,5 +30,13 @@ string UnitOfMeasure::getNamePlural() const{ } string UnitOfMeasure::getAbbreviation() const{ - return this->abbreviation; + return this->abbreviation; +} + +int UnitOfMeasure::getType() const{ + return this->type; +} + +double UnitOfMeasure::getMetricCoefficient() const{ + return this->metricCoefficient; } diff --git a/model/recipe/ingredients/unitofmeasure.h b/model/recipe/ingredients/unitofmeasure.h index 1787f14..b446e2e 100644 --- a/model/recipe/ingredients/unitofmeasure.h +++ b/model/recipe/ingredients/unitofmeasure.h @@ -12,8 +12,14 @@ using namespace std; class UnitOfMeasure { public: + //Constants Declarations. + static const int MASS = 1; + static const int VOLUME = 2; + static const int LENGTH = 3; + static const int MISC = 4; + //Full constructor. - UnitOfMeasure(string name, string plural, string abbreviation); + UnitOfMeasure(string name, string plural, string abbreviation, int type, double coef); //Attempt to guess unit from just a string. UnitOfMeasure(string name); //Constructor with default values. @@ -23,10 +29,14 @@ public: string getName() const; string getNamePlural() const; string getAbbreviation() const; + int getType() const; + double getMetricCoefficient() const; private: string name; //The name of the unit of measure. string plural; //The plural name. string abbreviation; //A short version of the unit. + int type; //The type of unit, as one of the constants above. + double metricCoefficient; //The conversion from this unit to the standard metric unit. }; #endif // UNITOFMEASURE_H diff --git a/model/recipe/tags/recipetag.cpp b/model/recipe/tags/recipetag.cpp index 22cd27d..3b1442a 100644 --- a/model/recipe/tags/recipetag.cpp +++ b/model/recipe/tags/recipetag.cpp @@ -8,7 +8,7 @@ RecipeTag::RecipeTag(string val){ this->value = val; } -string RecipeTag::getValue(){ +string RecipeTag::getValue() const{ return this->value; } diff --git a/model/recipe/tags/recipetag.h b/model/recipe/tags/recipetag.h index 5613af7..cd382fe 100644 --- a/model/recipe/tags/recipetag.h +++ b/model/recipe/tags/recipetag.h @@ -16,7 +16,7 @@ public: RecipeTag(string val); //Getters - string getValue(); + string getValue() const; //Setters void setValue(string newValue); private: diff --git a/model/recipe/tags/taglistmodel.cpp b/model/recipe/tags/taglistmodel.cpp new file mode 100644 index 0000000..dd70b3f --- /dev/null +++ b/model/recipe/tags/taglistmodel.cpp @@ -0,0 +1,44 @@ +#include "taglistmodel.h" + +TagListModel::TagListModel(){ + +} + +int TagListModel::rowCount(const QModelIndex &parent) const{ + return this->tags.size(); +} + +QVariant TagListModel::data(const QModelIndex &index, int role) const{ + string displayString = this->tags[index.row()].getValue(); + switch(role){ + case Qt::DisplayRole: + return QString::fromStdString(displayString); + } + + return QVariant(); +} + +void TagListModel::setTags(vector tags){ + this->tags = tags; + QModelIndex index = createIndex(0, 0); + QModelIndex bottomIndex = createIndex(this->tags.size()-1, 0); + emit dataChanged(index, bottomIndex); +} + +bool TagListModel::addTag(RecipeTag tag){ + //Add only if it's different. + for (unsigned int i = 0; i < this->tags.size(); i++){ + if (!this->tags[i].getValue().compare(tag.getValue())){ + return false; + } + } + this->tags.push_back(tag); + QModelIndex index = createIndex(this->tags.size()-1, 0); + QModelIndex bottomIndex = createIndex(this->tags.size()-1, 0); + emit dataChanged(index, bottomIndex); + return true; +} + +vector TagListModel::getTags(){ + return this->tags; +} diff --git a/model/recipe/tags/taglistmodel.h b/model/recipe/tags/taglistmodel.h new file mode 100644 index 0000000..e524e23 --- /dev/null +++ b/model/recipe/tags/taglistmodel.h @@ -0,0 +1,24 @@ +#ifndef TAGLISTMODEL_H +#define TAGLISTMODEL_H + +#include + +#include "model/recipe/tags/recipetag.h" + +class TagListModel : public QAbstractListModel +{ + public: + TagListModel(); + //Overridden methods. + int rowCount(const QModelIndex &parent = QModelIndex()) const override; + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; + + + void setTags(vector tags); + bool addTag(RecipeTag tag); + vector getTags(); + private: + vector tags; +}; + +#endif // TAGLISTMODEL_H