diff --git a/RecipeDB.pro b/RecipeDB.pro index 61f3bf6..4503a07 100644 --- a/RecipeDB.pro +++ b/RecipeDB.pro @@ -39,7 +39,8 @@ HEADERS += model/recipe/instruction.h \ SQLite/sqlite3ext.h \ model/database/resulttable.h \ model/database/recipedatabase.h \ - utils/fileutils.h + utils/fileutils.h \ + utils/stringutils.h LIBS += -ldl \ diff --git a/main.cpp b/main.cpp index f83a2c0..98c1306 100644 --- a/main.cpp +++ b/main.cpp @@ -12,18 +12,23 @@ int main(int argc, char *argv[]) w.show(); //TESTING CODE -// Database db("test.db"); -// printf("Table exists: %d\n", db.tableExists("ingredients")); -// db.executeSQL("SELECT * FROM ingredients;").printData(); -// db.executeSQL("PRAGMA table_info('ingredients');").printData(); -// db.executeSQL("SELECT name FROM ingredients WHERE foodGroup == 'fruit';").printData(); 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(); - FileUtils::saveInstruction(1, Instruction("This is some plain text with no HTML markup.")); + //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"))); + + Recipe rec("Example", ri, Instruction("BOLDiTaLiCs"), QImage(), vector(), QDate::currentDate(), QTime(0, 30), QTime(0, 25), 10.0f); + + w.loadFromRecipe(rec); return a.exec(); } diff --git a/model/database/database.cpp b/model/database/database.cpp index 8087e04..0b843d5 100644 --- a/model/database/database.cpp +++ b/model/database/database.cpp @@ -15,7 +15,7 @@ ResultTable Database::executeSQL(string statement){ this->returnCode = sqlite3_prepare_v2(this->db, statement.c_str(), -1, &stmt, NULL); ResultTable t; if (this->returnCode != SQLITE_OK){ - fprintf(stderr, "Unable to successfully prepare SQL statement. Error code: %d\nError Message: %s\n", this->returnCode, sqlite3_errmsg(this->db)); + fprintf(stderr, "Unable to successfully prepare SQL statement. Error code: %d\n\tError Message: %s\n", this->returnCode, sqlite3_errmsg(this->db)); return t; } @@ -26,6 +26,19 @@ ResultTable Database::executeSQL(string statement){ return t; } +bool Database::insertInto(string tableName, vector columnNames, vector values){ + if (columnNames.size() != values.size() || columnNames.empty()){ + return false; + } + string query = "INSERT INTO "+tableName+" ("; + string cols = combineVector(columnNames, ", "); + string vals = combineVector(values, ", "); + query += cols + ") VALUES (" + vals + ");"; + printf("Executing query: %s\n", query.c_str()); + this->executeSQL(query); + return true; +} + void Database::openConnection(){ this->returnCode = sqlite3_open(this->filename.c_str(), &this->db); if (this->returnCode || this->db == NULL){ @@ -39,7 +52,18 @@ void Database::openConnection(){ void Database::closeConnection(){ this->returnCode = sqlite3_close(this->db); - this->dbIsOpen = false; + this->dbIsOpen = false; +} + +string Database::combineVector(std::vector strings, string mid){ + if (strings.empty()){ + return ""; + } + std::string result = "'" + strings[0] + "'"; + for (std::vector::iterator it = strings.begin() + 1; it != strings.end(); ++it){ + result += mid + "'" + (*it) + "'"; + } + return result; } bool Database::tableExists(string tableName){ @@ -49,3 +73,7 @@ bool Database::tableExists(string tableName){ ResultTable t = executeSQL("SELECT name FROM sqlite_master WHERE type='table' AND name='"+tableName+"';"); return !t.isEmpty(); } + +int Database::getLastInsertedRowId(){ + return sqlite3_last_insert_rowid(this->db); +} diff --git a/model/database/database.h b/model/database/database.h index cd7ba1c..3c1b73f 100644 --- a/model/database/database.h +++ b/model/database/database.h @@ -3,6 +3,7 @@ #include #include +#include #include "SQLite/sqlite3.h" #include "model/recipe/ingredients/ingredient.h" @@ -23,8 +24,10 @@ public: //Executes an SQL string statement in a safe way and returns the result. ResultTable executeSQL(string statement); + bool insertInto(string tableName, vector columnNames, vector values); bool tableExists(string tableName); + int getLastInsertedRowId(); private: //SQL Instance variables. string filename; @@ -36,6 +39,7 @@ private: void openConnection(); void closeConnection(); + std::string combineVector(std::vector strings, std::string mid); }; #endif // DATABASE_H diff --git a/model/database/recipedatabase.cpp b/model/database/recipedatabase.cpp index ed312e8..5069fe0 100644 --- a/model/database/recipedatabase.cpp +++ b/model/database/recipedatabase.cpp @@ -6,7 +6,43 @@ RecipeDatabase::RecipeDatabase(string filename) : Database(filename){ void RecipeDatabase::storeRecipe(Recipe recipe){ ///TODO: Implement this in a smart way using transaction. + 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 { + bool success = this->insertInto("recipe", + vector({ + "name", + "createdDate", + "cookTime", + "prepTime", + "servingCount" + }), + vector({ + recipe.getName(), + recipe.getCreatedDate().toString().toStdString(), + recipe.getCookTime().toString().toStdString(), + recipe.getPrepTime().toString().toStdString(), + std::to_string(recipe.getServings()) + })); + if (success){ + //If successful, proceed to insert instructions, image, and ingredients. + } + } +} + +void RecipeDatabase::storeRecipeIngredient(RecipeIngredient ri){ + +} + +void RecipeDatabase::storeIngredient(Ingredient ingredient){ + ResultTable t = this->executeSQL("SELECT * FROM ingredient WHERE name='"+ingredient.getName()+"';"); + if (!t.isEmpty()){ + fprintf(stderr, "Error during storeIngredient: ingredient with name %s already exists.\n", ingredient.getName().c_str()); + } else { + this->insertInto("ingredient", vector({"foodGroup", "name"}), vector({ingredient.getFoodGroup(), ingredient.getName()})); + } } void RecipeDatabase::ensureTablesExist(){ @@ -17,24 +53,14 @@ void RecipeDatabase::ensureTablesExist(){ "ingredientId INTEGER PRIMARY KEY," "foodGroup varchar," "name varchar);"); - //Images table. - this->executeSQL("CREATE TABLE IF NOT EXISTS image(" - "imageNr INTEGER PRIMARY KEY," - "contentURL varchar);"); - //Instructions table. - this->executeSQL("CREATE TABLE IF NOT EXISTS instruction(" - "instructionNr INTEGER PRIMARY KEY," - "contentURL varchar);"); - //Recipe table. + //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," - "date date," + "createdDate date," "name varchar," - "imageNr int," "cookTime time," "prepTime time," - "servingCount real," - "FOREIGN KEY (imageNr) REFERENCES image(imageNr));"); + "servingCount real);"); //Recipe tags table. this->executeSQL("CREATE TABLE IF NOT EXISTS recipeTag(" "recipeId INTEGER PRIMARY KEY," @@ -49,27 +75,4 @@ void RecipeDatabase::ensureTablesExist(){ "comment varchar," "FOREIGN KEY (ingredientId) REFERENCES ingredient(ingredientId)," "FOREIGN KEY (recipeId) REFERENCES recipe(recipeId));"); - //Recipe Instruction mapping table. - this->executeSQL("CREATE TABLE IF NOT EXISTS recipeInstruction(" - "instructionNr int," - "recipeId INTEGER PRIMARY KEY," - "FOREIGN KEY (instructionNr) REFERENCES instruction(instructionNr)," - "FOREIGN KEY (recipeId) REFERENCES recipe(recipeId));"); -} - -void RecipeDatabase::storeInstruction(Instruction instruction){ - -} - -void RecipeDatabase::storeImage(QImage image){ - -} - -void RecipeDatabase::storeIngredient(Ingredient ingredient){ - ResultTable t = this->executeSQL("SELECT * FROM ingredient WHERE name='"+ingredient.getName()+"';"); - if (!t.isEmpty()){ - fprintf(stderr, "Error during storeIngredient: ingredient with name %s already exists.\n", ingredient.getName().c_str()); - } else { - this->executeSQL("INSERT INTO ingredient (foodGroup, name) VALUES ('"+ ingredient.getFoodGroup() +"', '"+ ingredient.getName() +"');"); - } } diff --git a/model/database/recipedatabase.h b/model/database/recipedatabase.h index a0d311a..9c52876 100644 --- a/model/database/recipedatabase.h +++ b/model/database/recipedatabase.h @@ -21,8 +21,7 @@ class RecipeDatabase : public Database void storeRecipe(Recipe recipe); //SQL Helper methods. - void storeInstruction(Instruction instruction); - void storeImage(QImage image); + void storeRecipeIngredient(RecipeIngredient ri); void storeIngredient(Ingredient ingredient); private: diff --git a/model/database/resulttable.cpp b/model/database/resulttable.cpp index b8f4bca..8b4d144 100644 --- a/model/database/resulttable.cpp +++ b/model/database/resulttable.cpp @@ -25,10 +25,14 @@ void ResultTable::processRow(sqlite3_stmt *stmt){ } void ResultTable::printData(){ + if (this->isEmpty()){ + printf("Result table is empty.\n"); + return; + } printf("Printing table: %d by %d\n", this->rowCount(), this->columnCount()); for (unsigned int row = 0; row < this->rowCount(); row++){ for (unsigned int col = 0; col < this->columnCount(); col++){ - printf("| %s \t", this->values[row][col].c_str()); + printf("| %s ", this->values[row][col].c_str()); } printf("\n"); } diff --git a/userInterface/mainwindow.cpp b/userInterface/mainwindow.cpp index 53aab11..69a6189 100644 --- a/userInterface/mainwindow.cpp +++ b/userInterface/mainwindow.cpp @@ -6,16 +6,7 @@ MainWindow::MainWindow(QWidget *parent) : ui(new Ui::MainWindow){ ui->setupUi(this); - ui->ingredientsListView->setModel(&this->ingredientModel); - - //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"))); - - Recipe rec("Example", ri, Instruction("BOLDiTaLiCs"), QImage(), vector(), QDate::currentDate(), QTime(0, 30), QTime(0, 25), 10.0f); - - this->loadFromRecipe(rec); + ui->ingredientsListView->setModel(&this->ingredientModel); } MainWindow::~MainWindow(){ diff --git a/utils/fileutils.h b/utils/fileutils.h index ed23f19..cb55e09 100644 --- a/utils/fileutils.h +++ b/utils/fileutils.h @@ -19,7 +19,7 @@ namespace FileUtils { } } - void saveInstruction(int nr, Instruction instruction){ + string saveInstruction(int nr, Instruction instruction){ ensureAppDataFolderExists(); QString filename = appDataPath + QString::fromStdString(std::to_string(nr)) +".html"; QFile file(filename); @@ -27,8 +27,10 @@ namespace FileUtils { QTextStream stream(&file); stream< +#include + +namespace StringUtils{ + + + +} + +#endif // STRINGUTILS_H