From 446ff8f5bafb97da4d757cfb9a183eee018a3b06 Mon Sep 17 00:00:00 2001 From: andrewlalis Date: Sat, 3 Mar 2018 10:18:38 +0100 Subject: [PATCH] Storage is now complete. Retrieval is partially implemented. --- main.cpp | 5 +- model/database/recipedatabase.cpp | 64 ++++++++++++++++--- model/database/recipedatabase.h | 2 + model/recipe/ingredients/recipeingredient.cpp | 6 +- model/recipe/ingredients/recipeingredient.h | 1 + model/recipe/ingredients/unitofmeasure.cpp | 9 ++- model/recipe/ingredients/unitofmeasure.h | 2 + model/recipe/recipe.cpp | 32 +++++++++- model/recipe/recipe.h | 2 + 9 files changed, 107 insertions(+), 16 deletions(-) diff --git a/main.cpp b/main.cpp index e8814ef..d67a414 100644 --- a/main.cpp +++ b/main.cpp @@ -30,11 +30,10 @@ int main(int argc, char *argv[]) bool success = recipeDB.storeRecipe(rec); printf("Storage successful: %d\n", success); - recipeDB.executeSQL("SELECT * FROM recipeIngredient;").printData(); - Recipe reloadRec = recipeDB.retrieveRecipe("Example"); + reloadRec.print(); - w.loadFromRecipe(rec); + w.loadFromRecipe(reloadRec); return a.exec(); } diff --git a/model/database/recipedatabase.cpp b/model/database/recipedatabase.cpp index 99bb92c..4c991da 100644 --- a/model/database/recipedatabase.cpp +++ b/model/database/recipedatabase.cpp @@ -8,7 +8,6 @@ bool RecipeDatabase::storeRecipe(Recipe recipe){ ///TODO: Implement this in a smart way using transaction. this->executeSQL("BEGIN;"); ResultTable t = this->selectFrom("recipe", "*", "name="+surroundString(recipe.getName(), "'")); - //ResultTable t = this->executeSQL("SELECT * FROM recipe WHERE name='"+recipe.getName()+"';"); if (!t.isEmpty()){ fprintf(stderr, "Error storing recipe: Recipe with name %s already exists.\n", recipe.getName().c_str()); } else { @@ -28,7 +27,7 @@ bool RecipeDatabase::storeRecipe(Recipe recipe){ std::to_string(recipe.getServings()) })); if (success){ - //If successful, proceed to insert instructions, image, and ingredients. + //If successful, proceed to insert instructions, image, and ingredients, and tags. int recipeId = this->getLastInsertedRowId(); bool ingredientSuccess = true; for (unsigned int i = 0; i < recipe.getIngredients().size(); i++){ @@ -37,7 +36,10 @@ bool RecipeDatabase::storeRecipe(Recipe recipe){ break; } } - if (ingredientSuccess && this->storeInstruction(recipe.getInstruction(), recipeId) && this->storeImage(recipe.getImage(), recipeId)){ + if (ingredientSuccess && + this->storeInstruction(recipe.getInstruction(), recipeId) && + this->storeImage(recipe.getImage(), recipeId) && + this->storeTags(recipe.getTags(), recipeId)){ this->executeSQL("COMMIT;"); return true; } @@ -50,7 +52,6 @@ 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(), "'")); - //ResultTable t = this->executeSQL("SELECT ingredientId FROM ingredient WHERE name='"+ri.getName()+"';"); int ingId = 0; if (t.isEmpty()){ if (!this->insertInto("ingredient", vector({"foodGroup", "name"}), vector({ri.getFoodGroup(), ri.getName()}))){ @@ -79,7 +80,6 @@ bool RecipeDatabase::storeRecipeIngredient(RecipeIngredient ri, int recipeId){ void RecipeDatabase::storeIngredient(Ingredient ingredient){ ResultTable t = this->selectFrom("ingredient", "*", "name="+surroundString(ingredient.getName(), "'")); - //ResultTable t = this->executeSQL("SELECT * FROM ingredient WHERE name='"+ingredient.getName()+"';"); if (t.isEmpty()){ this->insertInto("ingredient", vector({"foodGroup", "name"}), vector({ingredient.getFoodGroup(), ingredient.getName()})); } @@ -93,6 +93,24 @@ bool RecipeDatabase::storeImage(QImage image, int recipeId){ return FileUtils::saveImage(recipeId, image); } +bool RecipeDatabase::storeTags(vector tags, int recipeId){ + for (vector::iterator it = tags.begin(); it != tags.end(); ++it){ + bool s = this->insertInto("recipeTag", + vector({ + "recipeId", + "tagName" + }), + vector({ + std::to_string(recipeId), + (*it).getValue() + })); + if (!s){ + return false; + } + } + return true; +} + Recipe RecipeDatabase::retrieveRecipe(string name){ ResultTable t = this->selectFrom("recipe", "*", "name="+surroundString(name, "'")); if (t.isEmpty()){ @@ -100,12 +118,39 @@ Recipe RecipeDatabase::retrieveRecipe(string name){ return Recipe(); } t.printData(); - return Recipe(); + Recipe r; + int id = std::stoi(t.valueAt(0, 0)); + r.setName(t.valueAt(0, 1)); + r.setCreatedDate(QDate::fromString(QString::fromStdString(t.valueAt(0, 2)))); + r.setPrepTime(QTime::fromString(QString::fromStdString(t.valueAt(0, 3)))); + r.setCookTime(QTime::fromString(QString::fromStdString(t.valueAt(0, 4)))); + r.setServings(std::stof(t.valueAt(0, 5))); + r.setInstruction(FileUtils::loadInstruction(id)); + r.setImage(FileUtils::loadImage(id)); + r.setIngredients(this->retrieveRecipeIngredients(id)); + return r; +} + +vector RecipeDatabase::retrieveRecipeIngredients(int recipeId){ + ResultTable t = this->executeSQL("SELECT ingredient.name, ingredient.foodGroup, recipeIngredient.quantity, recipeIngredient.unitName, recipeIngredient.comment " + "FROM ingredient " + "INNER JOIN recipeIngredient " + "ON ingredient.ingredientId = recipeIngredient.ingredientId " + "AND recipeIngredient.recipeId = "+std::to_string(recipeId)+";"); + t.printData(); + 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))); + ings.push_back(r); + } + return ings; } void RecipeDatabase::ensureTablesExist(){ //Make sure that foreign keys are enabled. this->executeSQL("PRAGMA foreign_keys = ON;"); + + this->executeSQL("BEGIN;"); //Ingredients table. this->executeSQL("CREATE TABLE IF NOT EXISTS ingredient(" "ingredientId INTEGER PRIMARY KEY," @@ -114,14 +159,14 @@ void RecipeDatabase::ensureTablesExist(){ //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," - "createdDate date," "name varchar UNIQUE," - "cookTime time," + "createdDate date," "prepTime time," + "cookTime time," "servingCount real);"); //Recipe tags table. this->executeSQL("CREATE TABLE IF NOT EXISTS recipeTag(" - "recipeId INTEGER PRIMARY KEY," + "recipeId int," "tagName varchar," "FOREIGN KEY (recipeId) REFERENCES recipe(recipeId));"); //RecipeIngredient table. @@ -133,4 +178,5 @@ void RecipeDatabase::ensureTablesExist(){ "comment varchar," "FOREIGN KEY (ingredientId) REFERENCES ingredient(ingredientId)," "FOREIGN KEY (recipeId) REFERENCES recipe(recipeId));"); + this->executeSQL("COMMIT;"); } diff --git a/model/database/recipedatabase.h b/model/database/recipedatabase.h index 4c4d420..e91b45d 100644 --- a/model/database/recipedatabase.h +++ b/model/database/recipedatabase.h @@ -26,8 +26,10 @@ class RecipeDatabase : public Database void storeIngredient(Ingredient ingredient); bool storeInstruction(Instruction instruction, int recipeId); bool storeImage(QImage image, int recipeId); + bool storeTags(vector tags, int recipeId); Recipe retrieveRecipe(string name); + vector retrieveRecipeIngredients(int recipeId); private: //Utility methods. diff --git a/model/recipe/ingredients/recipeingredient.cpp b/model/recipe/ingredients/recipeingredient.cpp index 3e09886..c48155c 100644 --- a/model/recipe/ingredients/recipeingredient.cpp +++ b/model/recipe/ingredients/recipeingredient.cpp @@ -9,7 +9,11 @@ RecipeIngredient::RecipeIngredient(Ingredient i, float quantity, UnitOfMeasure u setName(i.getName()); setFoodGroup(i.getFoodGroup()); setQuantity(quantity); - setUnit(unit); + setUnit(unit); +} + +RecipeIngredient::RecipeIngredient(){ + } float RecipeIngredient::getQuantity() const{ diff --git a/model/recipe/ingredients/recipeingredient.h b/model/recipe/ingredients/recipeingredient.h index 54224b3..70c20f6 100644 --- a/model/recipe/ingredients/recipeingredient.h +++ b/model/recipe/ingredients/recipeingredient.h @@ -19,6 +19,7 @@ public: RecipeIngredient(string name, string foodGroup, float quantity, UnitOfMeasure unit); //Constructor using data from a child ingredient. RecipeIngredient(Ingredient i, float quantity, UnitOfMeasure unit); + RecipeIngredient(); //Getters float getQuantity() const; diff --git a/model/recipe/ingredients/unitofmeasure.cpp b/model/recipe/ingredients/unitofmeasure.cpp index ffc0066..ddf62c8 100644 --- a/model/recipe/ingredients/unitofmeasure.cpp +++ b/model/recipe/ingredients/unitofmeasure.cpp @@ -3,7 +3,14 @@ UnitOfMeasure::UnitOfMeasure(string name, string plural, string abbreviation){ this->name = name; this->plural = plural; - this->abbreviation = abbreviation; + this->abbreviation = abbreviation; +} + +UnitOfMeasure::UnitOfMeasure(string name){ + this->name = name; + this->plural = name + "s"; + this->abbreviation = "NULL"; + ///TODO: Make actual guessing of this stuff. } UnitOfMeasure::UnitOfMeasure() : UnitOfMeasure::UnitOfMeasure("", "", ""){ diff --git a/model/recipe/ingredients/unitofmeasure.h b/model/recipe/ingredients/unitofmeasure.h index 2692e5a..1787f14 100644 --- a/model/recipe/ingredients/unitofmeasure.h +++ b/model/recipe/ingredients/unitofmeasure.h @@ -14,6 +14,8 @@ class UnitOfMeasure public: //Full constructor. UnitOfMeasure(string name, string plural, string abbreviation); + //Attempt to guess unit from just a string. + UnitOfMeasure(string name); //Constructor with default values. UnitOfMeasure(); diff --git a/model/recipe/recipe.cpp b/model/recipe/recipe.cpp index 1c6754d..f99e1a2 100644 --- a/model/recipe/recipe.cpp +++ b/model/recipe/recipe.cpp @@ -29,7 +29,11 @@ Instruction Recipe::getInstruction() const{ } QImage Recipe::getImage() const{ - return this->image; + return this->image; +} + +vector Recipe::getTags() const{ + return this->tags; } QDate Recipe::getCreatedDate() const{ @@ -89,5 +93,29 @@ void Recipe::setCookTime(QTime newTime){ } void Recipe::setServings(float newServingsCount){ - this->servings = newServingsCount; + this->servings = newServingsCount; +} + +void Recipe::print(){ + printf("Recipe: %s, Created on: %s, Prep time: %s, Cook time: %s, Serves: %f\n", + this->name.c_str(), + this->createdDate.toString().toStdString().c_str(), + this->prepTime.toString().toStdString().c_str(), + this->cookTime.toString().toStdString().c_str(), + this->servings); + printf("\tInstruction: %s\n", this->instruction.getHTML().c_str()); + printf("\tIngredients:\n"); + for (vector::iterator it = this->ingredients.begin(); it != this->ingredients.end(); ++it){ + RecipeIngredient ri = *it; + printf("\t\t%s, Food Group: %s, Quantity: %f, Unit: %s\n", + ri.getName().c_str(), + ri.getFoodGroup().c_str(), + ri.getQuantity(), + ri.getUnit().getName().c_str()); + } + printf("\tTags:\n"); + for (vector::iterator it = this->tags.begin(); it != this->tags.end(); ++it){ + RecipeTag t = *it; + printf("\t\t%s\n", t.getValue().c_str()); + } } diff --git a/model/recipe/recipe.h b/model/recipe/recipe.h index 1fef395..d013659 100644 --- a/model/recipe/recipe.h +++ b/model/recipe/recipe.h @@ -58,6 +58,8 @@ public: void setPrepTime(QTime newTime); void setCookTime(QTime newTime); void setServings(float newServingsCount); + + void print(); private: //Main information. string name; //The name of the recipe.