2018-03-01 16:19:13 +00:00
|
|
|
#include "recipedatabase.h"
|
|
|
|
|
|
|
|
RecipeDatabase::RecipeDatabase(string filename) : Database(filename){
|
|
|
|
this->ensureTablesExist();
|
|
|
|
}
|
|
|
|
|
2018-03-02 13:14:56 +00:00
|
|
|
bool RecipeDatabase::storeRecipe(Recipe recipe){
|
2018-03-01 16:28:18 +00:00
|
|
|
///TODO: Implement this in a smart way using transaction.
|
2018-03-03 07:38:32 +00:00
|
|
|
this->executeSQL("BEGIN;");
|
|
|
|
ResultTable t = this->selectFrom("recipe", "*", "name="+surroundString(recipe.getName(), "'"));
|
2018-03-02 10:30:16 +00:00
|
|
|
if (!t.isEmpty()){
|
|
|
|
fprintf(stderr, "Error storing recipe: Recipe with name %s already exists.\n", recipe.getName().c_str());
|
|
|
|
} else {
|
|
|
|
bool success = this->insertInto("recipe",
|
|
|
|
vector<string>({
|
|
|
|
"name",
|
|
|
|
"createdDate",
|
|
|
|
"cookTime",
|
|
|
|
"prepTime",
|
|
|
|
"servingCount"
|
|
|
|
}),
|
|
|
|
vector<string>({
|
|
|
|
recipe.getName(),
|
|
|
|
recipe.getCreatedDate().toString().toStdString(),
|
|
|
|
recipe.getCookTime().toString().toStdString(),
|
|
|
|
recipe.getPrepTime().toString().toStdString(),
|
|
|
|
std::to_string(recipe.getServings())
|
|
|
|
}));
|
|
|
|
if (success){
|
2018-03-03 09:18:38 +00:00
|
|
|
//If successful, proceed to insert instructions, image, and ingredients, and tags.
|
2018-03-02 13:14:56 +00:00
|
|
|
int recipeId = this->getLastInsertedRowId();
|
2018-03-03 07:38:32 +00:00
|
|
|
bool ingredientSuccess = true;
|
2018-03-02 13:14:56 +00:00
|
|
|
for (unsigned int i = 0; i < recipe.getIngredients().size(); i++){
|
|
|
|
if (!this->storeRecipeIngredient(recipe.getIngredients()[i], recipeId)){
|
2018-03-03 07:38:32 +00:00
|
|
|
ingredientSuccess = false;
|
|
|
|
break;
|
2018-03-02 13:14:56 +00:00
|
|
|
}
|
|
|
|
}
|
2018-03-03 09:18:38 +00:00
|
|
|
if (ingredientSuccess &&
|
|
|
|
this->storeInstruction(recipe.getInstruction(), recipeId) &&
|
|
|
|
this->storeImage(recipe.getImage(), recipeId) &&
|
|
|
|
this->storeTags(recipe.getTags(), recipeId)){
|
2018-03-03 07:38:32 +00:00
|
|
|
this->executeSQL("COMMIT;");
|
|
|
|
return true;
|
2018-03-02 13:14:56 +00:00
|
|
|
}
|
2018-03-02 10:30:16 +00:00
|
|
|
}
|
|
|
|
}
|
2018-03-03 07:38:32 +00:00
|
|
|
this->executeSQL("ROLLBACK;");
|
|
|
|
return false;
|
2018-03-02 10:30:16 +00:00
|
|
|
}
|
|
|
|
|
2018-03-02 13:14:56 +00:00
|
|
|
bool RecipeDatabase::storeRecipeIngredient(RecipeIngredient ri, int recipeId){
|
|
|
|
//First check if the base ingredient has been added to the database. This is done within storeIngredient().
|
2018-03-03 07:38:32 +00:00
|
|
|
ResultTable t = this->selectFrom("ingredient", "ingredientId", "name="+surroundString(ri.getName(), "'"));
|
2018-03-02 13:14:56 +00:00
|
|
|
int ingId = 0;
|
|
|
|
if (t.isEmpty()){
|
|
|
|
if (!this->insertInto("ingredient", vector<string>({"foodGroup", "name"}), vector<string>({ri.getFoodGroup(), ri.getName()}))){
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
ingId = this->getLastInsertedRowId();
|
|
|
|
} else {
|
|
|
|
ingId = std::stoi(t.valueAt(0, 0));
|
|
|
|
}
|
|
|
|
return this->insertInto("recipeIngredient",
|
|
|
|
vector<string>({
|
|
|
|
"ingredientId",
|
|
|
|
"recipeId",
|
|
|
|
"quantity",
|
|
|
|
"unitName",
|
|
|
|
"comment"
|
|
|
|
}),
|
|
|
|
vector<string>({
|
|
|
|
std::to_string(ingId),
|
|
|
|
std::to_string(recipeId),
|
|
|
|
std::to_string(ri.getQuantity()),
|
|
|
|
ri.getUnit().getName(),
|
|
|
|
ri.getComment()
|
|
|
|
}));
|
2018-03-01 16:28:18 +00:00
|
|
|
}
|
|
|
|
|
2018-03-02 10:30:16 +00:00
|
|
|
void RecipeDatabase::storeIngredient(Ingredient ingredient){
|
2018-03-03 07:38:32 +00:00
|
|
|
ResultTable t = this->selectFrom("ingredient", "*", "name="+surroundString(ingredient.getName(), "'"));
|
2018-03-02 13:14:56 +00:00
|
|
|
if (t.isEmpty()){
|
2018-03-02 10:30:16 +00:00
|
|
|
this->insertInto("ingredient", vector<string>({"foodGroup", "name"}), vector<string>({ingredient.getFoodGroup(), ingredient.getName()}));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-03-02 13:14:56 +00:00
|
|
|
bool RecipeDatabase::storeInstruction(Instruction instruction, int recipeId){
|
2018-03-03 07:38:32 +00:00
|
|
|
return FileUtils::saveInstruction(recipeId, instruction);
|
2018-03-02 13:14:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
bool RecipeDatabase::storeImage(QImage image, int recipeId){
|
|
|
|
return FileUtils::saveImage(recipeId, image);
|
|
|
|
}
|
|
|
|
|
2018-03-03 09:18:38 +00:00
|
|
|
bool RecipeDatabase::storeTags(vector<RecipeTag> tags, int recipeId){
|
|
|
|
for (vector<RecipeTag>::iterator it = tags.begin(); it != tags.end(); ++it){
|
|
|
|
bool s = this->insertInto("recipeTag",
|
|
|
|
vector<string>({
|
|
|
|
"recipeId",
|
|
|
|
"tagName"
|
|
|
|
}),
|
|
|
|
vector<string>({
|
|
|
|
std::to_string(recipeId),
|
|
|
|
(*it).getValue()
|
|
|
|
}));
|
|
|
|
if (!s){
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2018-03-03 07:48:55 +00:00
|
|
|
Recipe RecipeDatabase::retrieveRecipe(string name){
|
|
|
|
ResultTable t = this->selectFrom("recipe", "*", "name="+surroundString(name, "'"));
|
|
|
|
if (t.isEmpty()){
|
|
|
|
fprintf(stderr, "Error: No recipe with name %s found!\n", name.c_str());
|
|
|
|
return Recipe();
|
|
|
|
}
|
2018-03-03 09:18:38 +00:00
|
|
|
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<RecipeIngredient> 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)+";");
|
|
|
|
vector<RecipeIngredient> 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;
|
2018-03-03 07:48:55 +00:00
|
|
|
}
|
|
|
|
|
2018-03-01 16:19:13 +00:00
|
|
|
void RecipeDatabase::ensureTablesExist(){
|
|
|
|
//Make sure that foreign keys are enabled.
|
|
|
|
this->executeSQL("PRAGMA foreign_keys = ON;");
|
2018-03-03 09:18:38 +00:00
|
|
|
|
|
|
|
this->executeSQL("BEGIN;");
|
2018-03-01 16:19:13 +00:00
|
|
|
//Ingredients table.
|
|
|
|
this->executeSQL("CREATE TABLE IF NOT EXISTS ingredient("
|
2018-03-02 08:32:40 +00:00
|
|
|
"ingredientId INTEGER PRIMARY KEY,"
|
2018-03-01 16:19:13 +00:00
|
|
|
"foodGroup varchar,"
|
2018-03-03 07:38:32 +00:00
|
|
|
"name varchar UNIQUE);");
|
2018-03-02 10:30:16 +00:00
|
|
|
//Recipe table. Each recipe can have at most one instruction, and one image.
|
2018-03-01 16:19:13 +00:00
|
|
|
this->executeSQL("CREATE TABLE IF NOT EXISTS recipe("
|
2018-03-02 08:32:40 +00:00
|
|
|
"recipeId INTEGER PRIMARY KEY,"
|
2018-03-03 07:38:32 +00:00
|
|
|
"name varchar UNIQUE,"
|
2018-03-03 09:18:38 +00:00
|
|
|
"createdDate date,"
|
2018-03-01 16:19:13 +00:00
|
|
|
"prepTime time,"
|
2018-03-03 09:18:38 +00:00
|
|
|
"cookTime time,"
|
2018-03-02 10:30:16 +00:00
|
|
|
"servingCount real);");
|
2018-03-01 16:19:13 +00:00
|
|
|
//Recipe tags table.
|
|
|
|
this->executeSQL("CREATE TABLE IF NOT EXISTS recipeTag("
|
2018-03-03 09:18:38 +00:00
|
|
|
"recipeId int,"
|
2018-03-01 16:19:13 +00:00
|
|
|
"tagName varchar,"
|
|
|
|
"FOREIGN KEY (recipeId) REFERENCES recipe(recipeId));");
|
|
|
|
//RecipeIngredient table.
|
|
|
|
this->executeSQL("CREATE TABLE IF NOT EXISTS recipeIngredient("
|
|
|
|
"ingredientId int,"
|
2018-03-02 13:14:56 +00:00
|
|
|
"recipeId int,"
|
2018-03-01 16:19:13 +00:00
|
|
|
"quantity real,"
|
|
|
|
"unitName varchar,"
|
|
|
|
"comment varchar,"
|
|
|
|
"FOREIGN KEY (ingredientId) REFERENCES ingredient(ingredientId),"
|
|
|
|
"FOREIGN KEY (recipeId) REFERENCES recipe(recipeId));");
|
2018-03-03 09:18:38 +00:00
|
|
|
this->executeSQL("COMMIT;");
|
2018-03-02 08:32:40 +00:00
|
|
|
}
|