diff --git a/gui/mainwindow.cpp b/gui/mainwindow.cpp index d2de990..c552432 100644 --- a/gui/mainwindow.cpp +++ b/gui/mainwindow.cpp @@ -19,19 +19,24 @@ MainWindow::~MainWindow(){ } void MainWindow::loadFromRecipe(Recipe recipe){ - setRecipeName(recipe.getName()); - setInstruction(recipe.getInstruction()); - setIngredients(recipe.getIngredients()); - if (recipe.getImage().isNull()){ - setImage(QImage(QString(":/images/images/no_image.png"))); + if (recipe.isEmpty()){ + setRecipeName("No recipes found."); + setAuthorName("Click 'New' to get started."); } else { - setImage(recipe.getImage()); + setRecipeName(recipe.getName()); + setInstruction(recipe.getInstruction()); + setIngredients(recipe.getIngredients()); + if (recipe.getImage().isNull()){ + setImage(QImage(QString(":/images/images/no_image.png"))); + } else { + setImage(recipe.getImage()); + } + setPrepTime(recipe.getPrepTime()); + setCookTime(recipe.getCookTime()); + setServings(recipe.getServings()); + setTags(recipe.getTags()); + this->currentRecipe = recipe; } - setPrepTime(recipe.getPrepTime()); - setCookTime(recipe.getCookTime()); - setServings(recipe.getServings()); - setTags(recipe.getTags()); - this->currentRecipe = recipe; } void MainWindow::setRecipeName(string name){ @@ -66,6 +71,10 @@ void MainWindow::setTags(vector tags){ this->tagsListModel.setTags(tags); } +void MainWindow::setAuthorName(string name){ + ui->authorLabel->setText(QString::fromStdString(name)); +} + void MainWindow::on_newButton_clicked(){ NewRecipeDialog d(this->recipeDB, this); d.show(); @@ -95,11 +104,12 @@ void MainWindow::on_exitButton_clicked(){ void MainWindow::on_editButton_clicked(){ NewRecipeDialog d(this->recipeDB, this->currentRecipe, this); + string originalName = this->currentRecipe.getName(); d.show(); d.exec(); if (d.isAccepted()){ Recipe r = d.getRecipe(); - if (!this->recipeDB->storeRecipe(r)){ + if (!this->recipeDB->updateRecipe(r, originalName)){ QMessageBox::critical(this, QString("Unable to Save Recipe"), QString("The program was not able to successfully save the recipe. Make sure to give the recipe a name, instructions, and some ingredients!")); } else { this->loadFromRecipe(r); diff --git a/gui/mainwindow.h b/gui/mainwindow.h index 3ce5cf4..70ab7be 100644 --- a/gui/mainwindow.h +++ b/gui/mainwindow.h @@ -54,6 +54,7 @@ public: void setCookTime(QTime cookTime); void setServings(float servings); void setTags(vector tags); + void setAuthorName(string name); }; #endif // MAINWINDOW_H diff --git a/gui/mainwindow.ui b/gui/mainwindow.ui index 23922da..69bddb9 100644 --- a/gui/mainwindow.ui +++ b/gui/mainwindow.ui @@ -364,7 +364,7 @@ font: "Noto Sans CJK KR"; - false + true diff --git a/gui/newrecipedialog.cpp b/gui/newrecipedialog.cpp index 3825ad1..fc8657f 100644 --- a/gui/newrecipedialog.cpp +++ b/gui/newrecipedialog.cpp @@ -23,6 +23,7 @@ NewRecipeDialog::NewRecipeDialog(RecipeDatabase *db, QWidget *parent) : NewRecip NewRecipeDialog::NewRecipeDialog(RecipeDatabase *db, Recipe recipe, QWidget *parent) : NewRecipeDialog(db, parent){ ui->recipeNameEdit->setText(QString::fromStdString(recipe.getName())); + ui->authorNameEdit->setText(QString::fromStdString(recipe.getAuthor())); ui->prepTimeEdit->setTime(recipe.getPrepTime()); ui->cookTimeEdit->setTime(recipe.getCookTime()); ui->servingsSpinBox->setValue((double)recipe.getServings()); @@ -38,9 +39,10 @@ NewRecipeDialog::~NewRecipeDialog(){ Recipe NewRecipeDialog::getRecipe(){ Recipe r(ui->recipeNameEdit->text().toStdString(), + ui->authorNameEdit->text().toStdString(), this->ingredientListModel.getIngredients(), ui->instructionsTextEdit->toHtml().toStdString(), - this->img,//Image + ui->imageDisplayLabel->pixmap()->toImage(),//Image this->tagsListModel.getTags(),//Tags QDate::currentDate(), ui->prepTimeEdit->time(), @@ -126,7 +128,6 @@ void NewRecipeDialog::on_deleteTagButton_clicked(){ void NewRecipeDialog::on_selectImageButton_clicked(){ QString filename = QFileDialog::getOpenFileName(this, "Open Image", QString(), "Image Files (*.png *.jpg *.bmp)"); if (!filename.isEmpty()){ - this->img = QImage(filename); ui->imageDisplayLabel->setPixmap(QPixmap(filename)); } } diff --git a/gui/newrecipedialog.h b/gui/newrecipedialog.h index 105cd37..5198dd9 100644 --- a/gui/newrecipedialog.h +++ b/gui/newrecipedialog.h @@ -70,7 +70,6 @@ class NewRecipeDialog : public QDialog vector tags; RecipeIngredientListModel ingredientListModel; TagListModel tagsListModel; - QImage img; bool accepted = false; //Helper functions to fill fields. diff --git a/gui/newrecipedialog.ui b/gui/newrecipedialog.ui index 937ee33..50bee64 100644 --- a/gui/newrecipedialog.ui +++ b/gui/newrecipedialog.ui @@ -96,16 +96,25 @@ + + 0 + - 16 + 14 3 false false + + background-color: rgb(255, 255, 255); + + + false + Qt::AlignCenter @@ -114,6 +123,30 @@ + + + + + 12 + 3 + false + false + + + + background-color: rgb(245, 245, 255); + + + false + + + Qt::AlignCenter + + + Author + + + @@ -142,7 +175,7 @@ 0 1999 12 - 24 + 22 @@ -253,6 +286,9 @@ + + 2 + 0 @@ -286,6 +322,9 @@ Create a new tag + + background-color: rgb(255, 255, 255); + :/images/images/plus_icon.png:/images/images/plus_icon.png @@ -297,6 +336,9 @@ Permanently delete this tag + + background-color: rgb(255, 255, 255); + :/images/images/minus_icon.png:/images/images/minus_icon.png @@ -309,6 +351,9 @@ + + 0 + 0 @@ -323,6 +368,27 @@ + + + 0 + 20 + + + + Add the above tag to the recipe + + + QPushButton#addTagButton { + background-color: rgb(235, 235, 255); + border: 0px; +} +QPushButton#addTagButton:hover{ + background-color: rgb(245, 245, 255); +} +QPushButton#addTagButton:pressed{ + background-color: rgb(255, 255, 255); +} + Add @@ -330,8 +396,29 @@ + + + 0 + 20 + + + + Remove this tag from the recipe + + + QPushButton#deleteTagButton { + background-color: rgb(225, 225, 255); + border: 0px; +} +QPushButton#deleteTagButton:hover{ + background-color: rgb(235, 235, 255); +} +QPushButton#deleteTagButton:pressed{ + background-color: rgb(245, 245, 255); +} + - Delete + Remove @@ -352,6 +439,9 @@ QAbstractItemView::MultiSelection + + QAbstractItemView::ScrollPerPixel + @@ -432,7 +522,7 @@ - Add Ingredient + Ingredients Qt::AlignCenter @@ -495,6 +585,9 @@ + + Delete this ingredient + @@ -579,6 +672,9 @@ + + Create a new unit of measure + :/images/images/plus_icon.png:/images/images/plus_icon.png @@ -587,6 +683,9 @@ + + Delete this unit of measure + @@ -624,6 +723,9 @@ + + 0 + 0 @@ -638,6 +740,27 @@ + + + 0 + 30 + + + + Add the above ingredient to the recipe + + + QPushButton#addIngredientButton { + background-color: rgb(235, 235, 255); + border: 0px; +} +QPushButton#addIngredientButton:hover{ + background-color: rgb(245, 245, 255); +} +QPushButton#addIngredientButton:pressed{ + background-color: rgb(255, 255, 255); +} + Add @@ -645,8 +768,29 @@ + + + 0 + 30 + + + + Remove this ingredient from the recipe + + + QPushButton#removeIngredientButton { + background-color: rgb(225, 225, 255); + border: 0px; +} +QPushButton#removeIngredientButton:hover{ + background-color: rgb(235, 235, 255); +} +QPushButton#removeIngredientButton:pressed{ + background-color: rgb(245, 245, 255); +} + - Delete + Remove @@ -667,6 +811,9 @@ QAbstractItemView::MultiSelection + + QAbstractItemView::ScrollPerPixel + 100 @@ -746,16 +893,6 @@ background-color: rgb(250, 250, 255); - - - - Instructions - - - Qt::AlignCenter - - - @@ -769,7 +906,7 @@ - I + Italic true @@ -789,7 +926,7 @@ - B + Bold true @@ -827,7 +964,7 @@ Qt::LeftToRight - true + false background-color: rgb(255, 255, 255); diff --git a/gui/openrecipedialog.cpp b/gui/openrecipedialog.cpp index 184ea18..8f936da 100644 --- a/gui/openrecipedialog.cpp +++ b/gui/openrecipedialog.cpp @@ -72,7 +72,7 @@ void OpenRecipeDialog::on_deleteRecipeButton_clicked(){ } string recipePlural = (rows.size() == 1) ? "recipe" : "recipes"; QString title = QString::fromStdString("Delete " + recipePlural); - QString content = QString::fromStdString("Are you sure you wish to delete the selected "+recipePlural+"?"); + QString content = QString::fromStdString("Are you sure you wish to delete the selected "+recipePlural+"?\nAll deleted recipes are permanently deleted."); QMessageBox::StandardButton reply = QMessageBox::question(this, title, content); if (reply == QMessageBox::Yes){ for (int row : rows){ @@ -127,3 +127,15 @@ void OpenRecipeDialog::on_foodGroupsListWidget_itemSelectionChanged(){ } this->populateRecipesTable(this->recipeDB->retrieveRecipesWithFoodGroups(groups)); } + +void OpenRecipeDialog::on_clearSearchButton_clicked(){ + ui->nameEdit->clear(); + ui->foodGroupsListWidget->selectionModel()->clearSelection(); + ui->tagsListView->selectionModel()->clearSelection(); + ui->ingredientsListView->selectionModel()->clearSelection(); + this->populateRecipesTable(this->recipeDB->retrieveAllRecipes()); +} + +void OpenRecipeDialog::on_exitButton_clicked(){ + this->close(); +} diff --git a/gui/openrecipedialog.h b/gui/openrecipedialog.h index 8e63496..c6cf23c 100644 --- a/gui/openrecipedialog.h +++ b/gui/openrecipedialog.h @@ -38,6 +38,10 @@ class OpenRecipeDialog : public QDialog void on_foodGroupsListWidget_itemSelectionChanged(); + void on_clearSearchButton_clicked(); + + void on_exitButton_clicked(); + private: Ui::OpenRecipeDialog *ui; RecipeDatabase *recipeDB; diff --git a/gui/openrecipedialog.ui b/gui/openrecipedialog.ui index 93cb94b..06db1f0 100644 --- a/gui/openrecipedialog.ui +++ b/gui/openrecipedialog.ui @@ -17,13 +17,37 @@ :/images/images/icon.png:/images/images/icon.png + + font: 25 "Noto Sans CJK KR Light"; + true - + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + background-color: rgb(245, 245, 255); + + + 0 + @@ -39,13 +63,35 @@ QTabWidget::Rounded - 2 + 1 - + + + + :/images/images/tag.png:/images/images/tag.png + + + + Tags + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + @@ -74,10 +120,32 @@ + + + :/images/images/ingredients.png:/images/images/ingredients.png + + + + Ingredients + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + @@ -105,11 +173,33 @@ - + + + + :/images/images/foodPyramid.png:/images/images/foodPyramid.png + + + + Food Groups + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + @@ -132,6 +222,9 @@ + + background-color: rgb(240, 240, 255); + @@ -141,19 +234,114 @@ - + + + + 12 + 3 + false + false + + + + background-color: rgb(255, 255, 255); + + + false + + + Qt::AlignCenter + + + Recipe Name + + - + + + + 0 + 40 + + + + false + + + QPushButton#clearSearchButton { + background-color: rgb(235, 235, 255); + border: 0px; +} +QPushButton#clearSearchButton:hover{ + background-color: rgb(245, 245, 255); +} +QPushButton#clearSearchButton:pressed{ + background-color: rgb(255, 255, 255); +} + + + Clear search criteria + + + false + + + + + + + + 0 + 40 + + + + QPushButton#deleteRecipeButton { + background-color: rgb(225, 225, 255); + border: 0px; +} +QPushButton#deleteRecipeButton:hover{ + background-color: rgb(235, 235, 255); +} +QPushButton#deleteRecipeButton:pressed{ + background-color: rgb(245, 245, 255); +} + - :/images/images/search_icon.png:/images/images/search_icon.png + :/images/images/trash.png:/images/images/trash.png + + + + + + + + 0 + 40 + + + + QPushButton#exitButton { + background-color: rgb(215, 215, 255); + border: 0px; +} +QPushButton#exitButton:hover{ + background-color: rgb(225, 225, 255); +} +QPushButton#exitButton:pressed{ + background-color: rgb(235, 235, 255); +} + + + Close @@ -163,34 +351,55 @@ - - - - - - - - - - - :/images/images/trash.png:/images/images/trash.png - - - - - - + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + 14 + 3 + false + false + + + + background-color: rgb(250, 250, 255); + QFrame::NoFrame + + true + QAbstractItemView::SelectRows + + QAbstractItemView::ScrollPerPixel + + + Qt::NoPen + true + + false + false diff --git a/images.qrc b/images.qrc index a5261a4..de44dca 100644 --- a/images.qrc +++ b/images.qrc @@ -6,5 +6,8 @@ images/minus_icon.png images/search_icon.png images/trash.png + images/tag.png + images/foodPyramid.png + images/ingredients.png diff --git a/images/foodPyramid.png b/images/foodPyramid.png new file mode 100644 index 0000000..10ecfa6 Binary files /dev/null and b/images/foodPyramid.png differ diff --git a/images/ingredients.png b/images/ingredients.png new file mode 100644 index 0000000..5d8e207 Binary files /dev/null and b/images/ingredients.png differ diff --git a/images/tag.png b/images/tag.png new file mode 100644 index 0000000..544ec9a Binary files /dev/null and b/images/tag.png differ diff --git a/main.cpp b/main.cpp index 940cd9d..cdd2938 100644 --- a/main.cpp +++ b/main.cpp @@ -8,23 +8,27 @@ void test(RecipeDatabase *recipeDB); +Recipe checkForFirstRun(RecipeDatabase *recipeDB){ + Recipe r = recipeDB->retrieveRandomRecipe(); + if (r.isEmpty()){//There are no recipes in the database. + //Add some basic units to the units, and some basic ingredients. + recipeDB->addBasicUnits(); + recipeDB->addBasicIngredients(); + } + return r; +} + int main(int argc, char *argv[]) { RecipeDatabase recipeDB(QString(FileUtils::appDataPath+"recipes.db").toStdString()); - QApplication a(argc, argv); + + QApplication a(argc, argv); MainWindow w(&recipeDB); - w.show(); - - //TESTING CODE - test(&recipeDB); - - //END TESTING CODE. - - w.loadFromRecipe(recipeDB.retrieveRandomRecipe()); + w.loadFromRecipe(checkForFirstRun(&recipeDB)); + w.show(); a.exec(); recipeDB.closeConnection(); - printf("Total queries: %lu\n", recipeDB.getQueryCount()); return 0; } @@ -35,6 +39,7 @@ void test(RecipeDatabase *recipeDB){ ri.push_back(RecipeIngredient("baking powder", "additives", 1.0f, UnitOfMeasure("teaspoon", "teaspoons", "tsp", UnitOfMeasure::VOLUME, 1.0), "")); Recipe rec("Example", + "Andrew Lalis", ri, Instruction("Placeholder Text"), QImage(), @@ -48,18 +53,4 @@ void test(RecipeDatabase *recipeDB){ bool success = recipeDB->storeRecipe(rec); printf("Storage successful: %d\n", success); -// vector foodGroups = recipeDB->retrieveAllFoodGroups(); -// printf("Food Groups:\n"); -// for (string s : foodGroups){ -// printf("\t%s\n", s.c_str()); -// } - - //Get food groups from recipe. -// Recipe r = recipeDB->retrieveRecipe("Pannenkoeken"); -// vector foodGroupsR = r.getFoodGroups(); -// printf("Pannenkoeken Food Groups:\n"); -// for (string s : foodGroupsR){ -// printf("\t%s\n", s.c_str()); -// } - } diff --git a/model/database/database.cpp b/model/database/database.cpp index db53c3f..6e914b0 100644 --- a/model/database/database.cpp +++ b/model/database/database.cpp @@ -14,12 +14,11 @@ ResultTable Database::executeSQL(string statement){ sqlite3_stmt* stmt; this->sql = statement; this->returnCode = sqlite3_prepare_v2(this->db, statement.c_str(), -1, &stmt, NULL); - ResultTable t(statement); if (this->returnCode != SQLITE_OK){ fprintf(stderr, "Unable to successfully prepare SQL statement. Error code: %d\n\tError Message: %s\n", this->returnCode, sqlite3_errmsg(this->db)); - return t; + return ResultTable(this->returnCode); } - + ResultTable t(statement); t.extractData(stmt); this->returnCode = sqlite3_finalize(stmt); @@ -78,6 +77,18 @@ void Database::closeConnection(){ this->dbIsOpen = false; } +void Database::beginTransaction(){ + this->executeSQL("BEGIN;"); +} + +void Database::commitTransaction(){ + this->executeSQL("COMMIT;"); +} + +void Database::rollbackTransaction(){ + this->executeSQL("ROLLBACK;"); +} + string Database::combineVector(std::vector strings, string mid){ if (strings.empty()){ return ""; diff --git a/model/database/database.h b/model/database/database.h index d0bd435..0ccc9fd 100644 --- a/model/database/database.h +++ b/model/database/database.h @@ -35,6 +35,10 @@ public: void closeConnection(); + void beginTransaction(); + void commitTransaction(); + void rollbackTransaction(); + protected: string surroundString(string s, string surround); diff --git a/model/database/recipedatabase.cpp b/model/database/recipedatabase.cpp index 1a9f83a..bae0eae 100644 --- a/model/database/recipedatabase.cpp +++ b/model/database/recipedatabase.cpp @@ -12,7 +12,7 @@ bool RecipeDatabase::storeRecipe(Recipe recipe){ return false; } //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;"); + this->beginTransaction(); 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()); @@ -20,6 +20,7 @@ bool RecipeDatabase::storeRecipe(Recipe recipe){ bool success = this->insertInto("recipe", vector({ "name", + "authorName", "createdDate", "cookTime", "prepTime", @@ -27,6 +28,7 @@ bool RecipeDatabase::storeRecipe(Recipe recipe){ }), vector({ recipe.getName(), + recipe.getAuthor(), recipe.getCreatedDate().toString().toStdString(), recipe.getCookTime().toString().toStdString(), recipe.getPrepTime().toString().toStdString(), @@ -46,12 +48,12 @@ bool RecipeDatabase::storeRecipe(Recipe recipe){ this->storeInstruction(recipe.getInstruction(), recipeId) && this->storeImage(recipe.getImage(), recipeId) && this->storeTags(recipe.getTags(), recipeId)){ - this->executeSQL("COMMIT;"); + this->commitTransaction(); return true; } } } - this->executeSQL("ROLLBACK;"); + this->rollbackTransaction(); return false; } @@ -158,7 +160,7 @@ Recipe RecipeDatabase::retrieveRandomRecipe(){ } return this->readFromResultTable(t); } -//TODO: Change this to be more efficient! One query per recipe is not good! + vector RecipeDatabase::retrieveAllRecipes(){ ResultTable t = this->executeSQL("SELECT * FROM recipe ORDER BY name;"); return this->readRecipesFromTable(t); @@ -204,7 +206,7 @@ vector RecipeDatabase::retrieveRecipesWithTags(vector tags){ } vector RecipeDatabase::retrieveRecipesWithSubstring(string s){ - ResultTable t = this->executeSQL("SELECT * FROM recipe WHERE name LIKE '%"+s+"%' COLLATE NOCASE;"); + ResultTable t = this->executeSQL("SELECT * FROM recipe WHERE name LIKE '%"+s+"%' COLLATE NOCASE ORDER BY name;"); return this->readRecipesFromTable(t); } @@ -253,6 +255,18 @@ vector RecipeDatabase::retrieveRecipeIngredients(int recipeId) return ings; } +int RecipeDatabase::retrieveIngredientId(string ingredientName){ + return std::stoi(this->selectFrom("ingredient", "ingredientId", "WHERE name = '"+ingredientName+"'").at(0, 0)); +} + +bool RecipeDatabase::deleteRecipeTags(int recipeId){ + return this->deleteFrom("recipeTag", "WHERE recipeId = "+std::to_string(recipeId)); +} + +bool RecipeDatabase::deleteRecipeIngredients(int recipeId){ + return this->deleteFrom("recipeIngredient", "WHERE recipeId = "+std::to_string(recipeId)); +} + vector RecipeDatabase::retrieveAllIngredients(){ ResultTable t = this->selectFrom("ingredient", "name, foodGroup", "ORDER BY name"); vector ings; @@ -314,7 +328,7 @@ bool RecipeDatabase::deleteRecipe(int recipeId){ printf("Cannot delete. No recipe with ID %d exists.\n", recipeId); return false; } - this->executeSQL("BEGIN;"); + this->beginTransaction(); bool tagsDeleted = this->deleteFrom("recipeTag", "WHERE recipeId="+idString); bool recipeIngredientDeleted = this->deleteFrom("recipeIngredient", "WHERE recipeId="+idString); bool recipeDeleted = this->deleteFrom("recipe", "WHERE recipeId="+idString); @@ -323,10 +337,10 @@ bool RecipeDatabase::deleteRecipe(int recipeId){ Q_UNUSED(instructionDeleted); Q_UNUSED(imageDeleted); if (tagsDeleted && recipeIngredientDeleted && recipeDeleted){ - this->executeSQL("COMMIT;"); + this->commitTransaction(); return true; } else { - this->executeSQL("ROLLBACK;"); + this->rollbackTransaction(); return false; } } @@ -356,15 +370,123 @@ bool RecipeDatabase::deleteTag(RecipeTag tag){ return this->deleteFrom("recipeTag", "WHERE tagName='"+tag.getValue()+"'"); } -bool RecipeDatabase::updateRecipe(Recipe recipe){ +bool RecipeDatabase::updateRecipe(Recipe recipe, string originalName) { + string idS = this->selectFrom("recipe", "recipeId", "WHERE name="+surroundString(originalName, "'")).at(0, 0); + int id = std::stoi(idS); + this->beginTransaction(); + ResultTable t = this->executeSQL("UPDATE recipe " + "SET name = '"+recipe.getName()+"', " + "authorName = '"+recipe.getAuthor()+"', " + "createdDate = '"+recipe.getCreatedDate().toString().toStdString()+"', " + "prepTime = '"+recipe.getPrepTime().toString().toStdString()+"', " + "cookTime = '"+recipe.getCookTime().toString().toStdString()+"', " + "servingCount = "+std::to_string(recipe.getServings())+" " + "WHERE recipeId = "+idS+";"); + bool recipeSuccess = t.getReturnCode() == SQLITE_DONE; + if (!recipeSuccess){ + this->rollbackTransaction(); + return false; + } + bool tagsSuccess = this->deleteRecipeTags(id); + for (RecipeTag tag : recipe.getTags()){ + tagsSuccess = tagsSuccess && this->insertInto( + "recipeTag", + vector({ + "recipeId", + "tagName" + }), + vector({ + idS, + tag.getValue() + })); + } + if (!tagsSuccess){ + this->rollbackTransaction(); + return false; + } + bool ingredientsSuccess = this->deleteRecipeIngredients(id); + for (RecipeIngredient ri : recipe.getIngredients()){ + ingredientsSuccess = ingredientsSuccess && this->insertInto( + "recipeIngredient", + vector({ + "recipeId", + "ingredientId", + "unitName", + "quantity", + "comment" + }), + vector({ + idS, + std::to_string(this->retrieveIngredientId(ri.getName())), + ri.getUnit().getName(), + std::to_string(ri.getQuantity()), + ri.getComment() + })); + } + if (!ingredientsSuccess){ + this->rollbackTransaction(); + return false; + } + bool instructionSuccess = FileUtils::saveInstruction(id, recipe.getInstruction()); + bool imageSuccess = FileUtils::saveImage(id, recipe.getImage()); + if (!(instructionSuccess && imageSuccess)){ + this->rollbackTransaction(); + return false; + } else { + this->commitTransaction(); + return true; + } +} +bool RecipeDatabase::addBasicUnits(){ + this->beginTransaction(); + //Volume + this->storeUnitOfMeasure(UnitOfMeasure("Teaspoon", "Teaspoons", "tsp", UnitOfMeasure::VOLUME, 5.0)); + this->storeUnitOfMeasure(UnitOfMeasure("Tablespoon", "Tablespoons", "tbsp", UnitOfMeasure::VOLUME, 15.0)); + this->storeUnitOfMeasure(UnitOfMeasure("Fluid Ounce", "Fluid Ounces", "fl oz", UnitOfMeasure::VOLUME, 30.0)); + this->storeUnitOfMeasure(UnitOfMeasure("Cup", "Cups", "c", UnitOfMeasure::VOLUME, 250.0)); + this->storeUnitOfMeasure(UnitOfMeasure("Milliliter", "Milliliters", "mL", UnitOfMeasure::VOLUME, 1.0)); + this->storeUnitOfMeasure(UnitOfMeasure("Liter", "Liters", "L", UnitOfMeasure::VOLUME, 1000.0)); + this->storeUnitOfMeasure(UnitOfMeasure("Gallon", "Gallons", "gal", UnitOfMeasure::VOLUME, 3800.0)); + //Mass/Weight + this->storeUnitOfMeasure(UnitOfMeasure("Ounce", "Ounces", "oz", UnitOfMeasure::MASS, 28.0)); + this->storeUnitOfMeasure(UnitOfMeasure("Pound", "Pounds", "lb", UnitOfMeasure::MASS, 454.0)); + this->storeUnitOfMeasure(UnitOfMeasure("Gram", "Grams", "g", UnitOfMeasure::MASS, 1.0)); + this->storeUnitOfMeasure(UnitOfMeasure("Milligram", "Milligrams", "mg", UnitOfMeasure::MASS, 0.001)); + this->storeUnitOfMeasure(UnitOfMeasure("Kilogram", "Kilograms", "kg", UnitOfMeasure::MASS, 1000.0)); + //Length + this->storeUnitOfMeasure(UnitOfMeasure("Inch", "Inches", "in", UnitOfMeasure::LENGTH, 2.54)); + this->storeUnitOfMeasure(UnitOfMeasure("Centimeter", "Centimeters", "cm", UnitOfMeasure::LENGTH, 1.0)); + //MISC + this->storeUnitOfMeasure(UnitOfMeasure("Piece", "Pieces", "pc", UnitOfMeasure::MISC, 1.0)); + this->storeUnitOfMeasure(UnitOfMeasure("Item", "Items", "", UnitOfMeasure::MISC, 1.0)); + this->commitTransaction(); + return true; +} + +bool RecipeDatabase::addBasicIngredients(){ + this->beginTransaction(); + this->storeIngredient(Ingredient("Flour", "grains")); + this->storeIngredient(Ingredient("Eggs", "eggs")); + this->storeIngredient(Ingredient("Milk", "dairy")); + this->storeIngredient(Ingredient("Cheese", "dairy")); + this->storeIngredient(Ingredient("Salt", "spices")); + this->storeIngredient(Ingredient("Sugar", "sugars")); + this->storeIngredient(Ingredient("Vegetable Oil", "oils")); + this->storeIngredient(Ingredient("Olive Oil", "oils")); + this->storeIngredient(Ingredient("Water", "water")); + this->storeIngredient(Ingredient("Bell Pepper", "vegetables")); + this->storeIngredient(Ingredient("Onion", "vegetables")); + this->storeIngredient(Ingredient("Garlic", "spices")); + this->commitTransaction(); + return true; } void RecipeDatabase::ensureTablesExist(){ //Make sure that foreign keys are enabled. this->executeSQL("PRAGMA foreign_keys = ON;"); - this->executeSQL("BEGIN;"); + this->beginTransaction(); //Ingredients table. this->executeSQL("CREATE TABLE IF NOT EXISTS ingredient(" "ingredientId INTEGER PRIMARY KEY," @@ -381,6 +503,7 @@ void RecipeDatabase::ensureTablesExist(){ this->executeSQL("CREATE TABLE IF NOT EXISTS recipe(" "recipeId INTEGER PRIMARY KEY," "name varchar UNIQUE," + "authorName varchar," "createdDate date," "prepTime time," "cookTime time," @@ -400,18 +523,19 @@ void RecipeDatabase::ensureTablesExist(){ "FOREIGN KEY (ingredientId) REFERENCES ingredient(ingredientId)," "FOREIGN KEY (recipeId) REFERENCES recipe(recipeId)," "FOREIGN KEY (unitName) REFERENCES unitOfMeasure(name));"); - this->executeSQL("COMMIT;"); + this->commitTransaction(); } Recipe RecipeDatabase::readFromResultTable(ResultTable t, int tRow){ Recipe r; TableRow row = t.rows().at(tRow); - int id = std::stoi(row.at(0)); - r.setName(row.at(1)); - r.setCreatedDate(QDate::fromString(QString::fromStdString(row.at(2)))); - r.setPrepTime(QTime::fromString(QString::fromStdString(row.at(3)))); - r.setCookTime(QTime::fromString(QString::fromStdString(row.at(4)))); - r.setServings(std::stof(row.at(5))); + int id = std::stoi(row.at(0)); //id + r.setName(row.at(1)); //Name + r.setAuthor(row.at(2)); //author + r.setCreatedDate(QDate::fromString(QString::fromStdString(row.at(3)))); //createdDate + r.setPrepTime(QTime::fromString(QString::fromStdString(row.at(4)))); //prepTime + r.setCookTime(QTime::fromString(QString::fromStdString(row.at(5)))); //cookTime + r.setServings(std::stof(row.at(6))); //servings r.setInstruction(FileUtils::loadInstruction(id)); r.setImage(FileUtils::loadImage(id)); r.setIngredients(this->retrieveRecipeIngredients(id)); diff --git a/model/database/recipedatabase.h b/model/database/recipedatabase.h index c68b0c9..2452e42 100644 --- a/model/database/recipedatabase.h +++ b/model/database/recipedatabase.h @@ -26,9 +26,6 @@ class RecipeDatabase : public Database bool storeRecipeIngredient(RecipeIngredient ri, int recipeId); 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); @@ -39,10 +36,8 @@ class RecipeDatabase : public Database vector retrieveRecipesWithSubstring(string s); vector retrieveRecipesWithFoodGroups(vector groups); vector retrieveAllFoodGroups(); - vector retrieveRecipeIngredients(int recipeId); vector retrieveAllIngredients(); vector retrieveAllUnitsOfMeasure(); - vector retrieveTags(int recipeId); vector retrieveAllTags(); //Deletion. @@ -53,7 +48,11 @@ class RecipeDatabase : public Database bool deleteTag(RecipeTag tag); //Updating. - bool updateRecipe(Recipe recipe); + bool updateRecipe(Recipe recipe, string originalName); + + //Adding basic information at start. + bool addBasicUnits(); + bool addBasicIngredients(); private: //Utility methods. @@ -61,6 +60,20 @@ class RecipeDatabase : public Database //Read a recipe from a row of a result table. Recipe readFromResultTable(ResultTable t, int row=0); vector readRecipesFromTable(ResultTable t); + + //Storage + bool storeInstruction(Instruction instruction, int recipeId); + bool storeImage(QImage image, int recipeId); + bool storeTags(vector tags, int recipeId); + + //Retrieval + vector retrieveTags(int recipeId); + vector retrieveRecipeIngredients(int recipeId); + int retrieveIngredientId(string ingredientName); + + //Deletion + bool deleteRecipeTags(int recipeId); + bool deleteRecipeIngredients(int recipeId); }; #endif // RECIPEDATABASE_H diff --git a/model/database/resulttable.cpp b/model/database/resulttable.cpp index ac96d7b..a8a3ac0 100644 --- a/model/database/resulttable.cpp +++ b/model/database/resulttable.cpp @@ -8,6 +8,10 @@ ResultTable::ResultTable(string query){ this->originalQuery = query; } +ResultTable::ResultTable(int resultCode){ + this->queryCode = resultCode; +} + void ResultTable::extractData(sqlite3_stmt *stmt){ this->values.clear(); int res = sqlite3_step(stmt); @@ -30,6 +34,7 @@ void ResultTable::processRow(sqlite3_stmt *stmt){ } void ResultTable::printData(){ + printf("--> Result Code: [%d] <--\n", this->getReturnCode()); if (this->isEmpty()){ printf("Result table is empty.\n"); return; diff --git a/model/database/resulttable.h b/model/database/resulttable.h index a4dcd84..361f75c 100644 --- a/model/database/resulttable.h +++ b/model/database/resulttable.h @@ -21,6 +21,8 @@ class ResultTable ResultTable(); //Constructs a table with the original query saved. ResultTable(string query); + //Constructs an empty table with a result code. + ResultTable(int resultCode); //Gets all the data from the result set and stores it internally as strings. void extractData(sqlite3_stmt* stmt); diff --git a/model/recipe/recipe.cpp b/model/recipe/recipe.cpp index a899c1f..8d123e7 100644 --- a/model/recipe/recipe.cpp +++ b/model/recipe/recipe.cpp @@ -1,7 +1,8 @@ #include "model/recipe/recipe.h" -Recipe::Recipe(string name, vector ingredients, Instruction instruction, QImage image, vector tags, QDate createdDate, QTime prepTime, QTime cookTime, float servings){ +Recipe::Recipe(string name, string author, vector ingredients, Instruction instruction, QImage image, vector tags, QDate createdDate, QTime prepTime, QTime cookTime, float servings){ setName(name); + setAuthor(author); setIngredients(ingredients); setInstruction(instruction); setImage(image); @@ -12,12 +13,16 @@ Recipe::Recipe(string name, vector ingredients, Instruction in setServings(servings); } -Recipe::Recipe() : Recipe::Recipe("", vector(), Instruction(), QImage(), vector(), QDate::currentDate(), QTime(1, 0), QTime(0, 30), 10.0f){ +Recipe::Recipe() : Recipe::Recipe("", "", vector(), Instruction(), QImage(), vector(), QDate::currentDate(), QTime(), QTime(), 1.0f){ //Set default values when none are specified. } string Recipe::getName() const{ - return this->name; + return this->name; +} + +string Recipe::getAuthor() const{ + return this->authorName; } vector Recipe::getIngredients() const{ @@ -71,7 +76,11 @@ bool Recipe::isEmpty() const{ } void Recipe::setName(string newName){ - this->name = newName; + this->name = newName; +} + +void Recipe::setAuthor(string newName){ + this->authorName = newName; } void Recipe::setIngredients(vector ingredients){ @@ -111,8 +120,9 @@ void Recipe::setServings(float newServingsCount){ } void Recipe::print(){ - printf("Recipe: %s, Created on: %s, Prep time: %s, Cook time: %s, Serves: %f\n", + printf("Recipe: %s, Created on: %s, by %s, Prep time: %s, Cook time: %s, Serves: %f\n", this->name.c_str(), + this->authorName.c_str(), this->createdDate.toString().toStdString().c_str(), this->prepTime.toString().toStdString().c_str(), this->cookTime.toString().toStdString().c_str(), diff --git a/model/recipe/recipe.h b/model/recipe/recipe.h index 3bdb92d..4d8dca3 100644 --- a/model/recipe/recipe.h +++ b/model/recipe/recipe.h @@ -32,12 +32,13 @@ class Recipe { public: //Full constructor - Recipe(string name, vector ingredients, Instruction instruction, QImage image, vector tags, QDate createdDate, QTime prepTime, QTime cookTime, float servings); + Recipe(string name, string author, vector ingredients, Instruction instruction, QImage image, vector tags, QDate createdDate, QTime prepTime, QTime cookTime, float servings); //Constructor with default values. Recipe(); //Getters string getName() const; + string getAuthor() const; vector getIngredients() const; vector getFoodGroups() const; Instruction getInstruction() const; @@ -52,6 +53,7 @@ public: //Setters void setName(string newName); + void setAuthor(string newName); void setIngredients(vector ingredients); void setTags(vector tags); void addIngredient(RecipeIngredient newIngredient); @@ -66,6 +68,7 @@ public: private: //Main information. string name; //The name of the recipe. + string authorName; //The name of the author of this recipe. vector ingredients; //The list of ingredients in the recipe. Instruction instruction; //The instruction HTML document. QImage image; //An image displayed alongside the recipe.