diff --git a/RecipeDB.pro b/RecipeDB.pro index a717ed2..2c957a5 100644 --- a/RecipeDB.pro +++ b/RecipeDB.pro @@ -12,8 +12,7 @@ TARGET = RecipeDB TEMPLATE = app -SOURCES += SQLite/sqlite3.c \ - model/recipe/instruction.cpp \ +SOURCES += model/recipe/instruction.cpp \ model/recipe/recipe.cpp \ userInterface/mainwindow.cpp \ main.cpp \ @@ -22,11 +21,13 @@ SOURCES += SQLite/sqlite3.c \ model/recipe/ingredients/ingredient.cpp \ model/recipe/ingredients/ingredientlistmodel.cpp \ model/recipe/ingredients/recipeingredient.cpp \ - model/recipe/tags/recipetag.cpp + model/recipe/tags/recipetag.cpp \ + SQLite/sqlite3.c \ + model/database/resulttable.cpp \ + model/database/recipedatabase.cpp \ + utils/fileutils.cpp -HEADERS += SQLite/sqlite3.h \ - SQLite/sqlite3ext.h \ - model/recipe/instruction.h \ +HEADERS += model/recipe/instruction.h \ model/recipe/recipe.h \ userInterface/mainwindow.h \ model/database/database.h \ @@ -34,7 +35,14 @@ HEADERS += SQLite/sqlite3.h \ model/recipe/ingredients/ingredient.h \ model/recipe/ingredients/ingredientlistmodel.h \ model/recipe/ingredients/recipeingredient.h \ - model/recipe/tags/recipetag.h + model/recipe/tags/recipetag.h \ + SQLite/sqlite3.h \ + SQLite/sqlite3ext.h \ + model/database/resulttable.h \ + model/database/recipedatabase.h \ + utils/fileutils.h + +LIBS += -ldl \ FORMS += gui/mainwindow.ui diff --git a/main.cpp b/main.cpp index 4f87ea1..d67a414 100644 --- a/main.cpp +++ b/main.cpp @@ -2,6 +2,7 @@ #include #include "model/database/database.h" +#include "model/database/recipedatabase.h" int main(int argc, char *argv[]) { @@ -9,7 +10,30 @@ int main(int argc, char *argv[]) MainWindow w; w.show(); - Database db("test.db"); + //TESTING CODE - return a.exec(); + 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"))); + + Recipe rec("Example", ri, Instruction("BOLDiTaLiCs"), QImage(), vector(), QDate::currentDate(), QTime(0, 30), QTime(0, 25), 10.0f); + + bool success = recipeDB.storeRecipe(rec); + printf("Storage successful: %d\n", success); + + Recipe reloadRec = recipeDB.retrieveRecipe("Example"); + reloadRec.print(); + + w.loadFromRecipe(reloadRec); + + return a.exec(); } diff --git a/model/database/database.cpp b/model/database/database.cpp index 23716ab..6fb4394 100644 --- a/model/database/database.cpp +++ b/model/database/database.cpp @@ -3,16 +3,47 @@ Database::Database(string filename){ this->filename = filename; openConnection(); - //TESTING CODE - if (tableExists("ingredients")){ - printf("Ingredients table already exists.\n"); - } else { - printf("Couldn't find the ingredients table.\n"); - } } Database::~Database(){ - closeConnection(); + closeConnection(); +} + +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; + } + + t.extractData(stmt); + + this->returnCode = sqlite3_finalize(stmt); + + 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 + ");"; + ResultTable t = this->executeSQL(query); + return (t.getReturnCode() == SQLITE_DONE); +} + +ResultTable Database::selectFrom(string tableName, string columnNames, string conditions){ + if (columnNames.size() == 0 || tableName.empty()){ + return ResultTable(); + } + string query = "SELECT " + columnNames + " FROM " + tableName + " WHERE " + conditions + ";"; + return this->executeSQL(query); } void Database::openConnection(){ @@ -28,20 +59,33 @@ 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 = surroundString(strings[0], "'"); + for (std::vector::iterator it = strings.begin() + 1; it != strings.end(); ++it){ + result += mid + surroundString((*it), "'"); + } + return result; +} + +string Database::surroundString(string s, string surround){ + return surround+s+surround; } bool Database::tableExists(string tableName){ - if (this->dbIsOpen){ - this->sql = "SELECT name FROM sqlite_master WHERE type='table' AND name='"+tableName+"';"; - const char* str = this->sql.c_str(); - this->returnCode = sqlite3_exec(this->db, str, NULL, 0, &this->errorMsg); - if (this->returnCode == SQLITE_ERROR){ - fprintf(stderr, "Unable to select name from master table list: %s\n", this->errorMsg); - return false; - } else { - return true; - } - } - return false; + 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+"';"); + 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 dce935a..4a60447 100644 --- a/model/database/database.h +++ b/model/database/database.h @@ -3,19 +3,35 @@ #include #include +#include #include "SQLite/sqlite3.h" #include "model/recipe/ingredients/ingredient.h" +#include "resulttable.h" using namespace std; +/** + * @brief The Database class is responsible for generic abstraction of commonly used database features. + */ + class Database { public: + //Creates and opens a database connection to a file of the given name. If not there, this will generate a database. Database(string filename); - ~Database(); + ~Database(); - void insertIngredient(Ingredient); + //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); + ResultTable selectFrom(string tableName, string columnNames, string conditions); + + bool tableExists(string tableName); + int getLastInsertedRowId(); + +protected: + string surroundString(string s, string surround); private: //SQL Instance variables. @@ -27,12 +43,8 @@ private: char* errorMsg; void openConnection(); - void closeConnection(); - //Guarantees that all tables from the schema exist. - void ensureTablesExist(); - - //Utility methods. - bool tableExists(string tableName); + 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 new file mode 100644 index 0000000..4c991da --- /dev/null +++ b/model/database/recipedatabase.cpp @@ -0,0 +1,182 @@ +#include "recipedatabase.h" + +RecipeDatabase::RecipeDatabase(string filename) : Database(filename){ + this->ensureTablesExist(); +} + +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(), "'")); + 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, and tags. + int recipeId = this->getLastInsertedRowId(); + bool ingredientSuccess = true; + for (unsigned int i = 0; i < recipe.getIngredients().size(); i++){ + if (!this->storeRecipeIngredient(recipe.getIngredients()[i], recipeId)){ + ingredientSuccess = false; + break; + } + } + if (ingredientSuccess && + this->storeInstruction(recipe.getInstruction(), recipeId) && + this->storeImage(recipe.getImage(), recipeId) && + this->storeTags(recipe.getTags(), recipeId)){ + this->executeSQL("COMMIT;"); + return true; + } + } + } + this->executeSQL("ROLLBACK;"); + return false; +} + +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)); + } + return this->insertInto("recipeIngredient", + vector({ + "ingredientId", + "recipeId", + "quantity", + "unitName", + "comment" + }), + vector({ + std::to_string(ingId), + std::to_string(recipeId), + std::to_string(ri.getQuantity()), + ri.getUnit().getName(), + ri.getComment() + })); +} + +void RecipeDatabase::storeIngredient(Ingredient ingredient){ + ResultTable t = this->selectFrom("ingredient", "*", "name="+surroundString(ingredient.getName(), "'")); + if (t.isEmpty()){ + this->insertInto("ingredient", vector({"foodGroup", "name"}), vector({ingredient.getFoodGroup(), ingredient.getName()})); + } +} + +bool RecipeDatabase::storeInstruction(Instruction instruction, int recipeId){ + return FileUtils::saveInstruction(recipeId, instruction); +} + +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()){ + 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)); + 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," + "foodGroup varchar," + "name varchar UNIQUE);"); + //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," + "name varchar UNIQUE," + "createdDate date," + "prepTime time," + "cookTime time," + "servingCount real);"); + //Recipe tags table. + this->executeSQL("CREATE TABLE IF NOT EXISTS recipeTag(" + "recipeId int," + "tagName varchar," + "FOREIGN KEY (recipeId) REFERENCES recipe(recipeId));"); + //RecipeIngredient table. + this->executeSQL("CREATE TABLE IF NOT EXISTS recipeIngredient(" + "ingredientId int," + "recipeId int," + "quantity real," + "unitName varchar," + "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 new file mode 100644 index 0000000..e91b45d --- /dev/null +++ b/model/database/recipedatabase.h @@ -0,0 +1,39 @@ +#ifndef RECIPEDATABASE_H +#define RECIPEDATABASE_H + + + +#include "database.h" +#include "model/recipe/recipe.h" +#include "utils/fileutils.h" + +using namespace std; + +/** + * @brief The RecipeDatabase class represents the precise database used for the recipe storage, and is specialized. + */ + +class RecipeDatabase : public Database +{ + public: + RecipeDatabase(string filename); + + //Stores a full recipe in the database. + bool storeRecipe(Recipe recipe); + + //SQL Helper methods. + bool storeRecipeIngredient(RecipeIngredient ri, int recipeId); + 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. + void ensureTablesExist(); +}; + +#endif // RECIPEDATABASE_H diff --git a/model/database/resulttable.cpp b/model/database/resulttable.cpp new file mode 100644 index 0000000..9811d64 --- /dev/null +++ b/model/database/resulttable.cpp @@ -0,0 +1,92 @@ +#include "resulttable.h" + +ResultTable::ResultTable(){ + +} + +ResultTable::ResultTable(string query){ + this->originalQuery = query; +} + +void ResultTable::extractData(sqlite3_stmt *stmt){ + this->values.clear(); + int res = sqlite3_step(stmt); + while (res == SQLITE_ROW){ + processRow(stmt); + res = sqlite3_step(stmt); + } + this->queryCode = res; +} + +void ResultTable::processRow(sqlite3_stmt *stmt){ + int colCount = sqlite3_column_count(stmt); + vector currentRow; + + for (int i = 0; i < colCount; i++){ + currentRow.push_back(convertToString(sqlite3_column_value(stmt, i))); + } + + this->values.push_back(currentRow); +} + +void ResultTable::printData(){ + if (this->isEmpty()){ + printf("Result table is empty.\n"); + return; + } + printf("Printing table: [%d x %d]\t%s\n", this->rowCount(), this->columnCount(), this->originalQuery.c_str()); + for (unsigned int row = 0; row < this->rowCount(); row++){ + for (unsigned int col = 0; col < this->columnCount(); col++){ + printf("| %s ", this->values[row][col].c_str()); + } + printf("\n"); + } +} + +bool ResultTable::isEmpty(){ + return this->values.empty(); +} + +string ResultTable::valueAt(unsigned int row, unsigned int col){ + if (isIndexValid(row, col)){ + return this->values[row][col]; + } else { + fprintf(stderr, "Out of bounds error while trying to get value in result table at [%d, %d].\n", row, col); + return ""; + } +} + +int ResultTable::getReturnCode(){ + return this->queryCode; +} + +string ResultTable::getOriginalQuery(){ + return this->originalQuery; +} + +unsigned int ResultTable::columnCount(){ + if (this->isEmpty()){ + return 0; + } + return this->values[0].size(); +} + +unsigned int ResultTable::rowCount(){ + if (this->isEmpty()){ + return 0; + } + return this->values.size(); +} + +string ResultTable::convertToString(sqlite3_value *val){ + const unsigned char* raw_text = sqlite3_value_text(val); + if (raw_text == 0){ + return ""; + } + string st = (const char*) raw_text; + return st; +} + +bool ResultTable::isIndexValid(unsigned int row, unsigned int col){ + return (row < this->values.size()) && (col < this->values[0].size()); +} diff --git a/model/database/resulttable.h b/model/database/resulttable.h new file mode 100644 index 0000000..a138ea3 --- /dev/null +++ b/model/database/resulttable.h @@ -0,0 +1,46 @@ +#ifndef RESULTTABLE_H +#define RESULTTABLE_H + +#include +#include + +#include "SQLite/sqlite3.h" + +using namespace std; + +/** + * @brief The ResultTable class is an object that contains the results of an SQL query, in string form. + */ + +class ResultTable +{ + public: + //Constructs an empty table. + ResultTable(); + //Constructs a table with the original query saved. + ResultTable(string query); + + //Gets all the data from the result set and stores it internally as strings. + void extractData(sqlite3_stmt* stmt); + //Stores the information from one row of a result set. + void processRow(sqlite3_stmt* stmt); + //Displays the data somewhat legibly. + void printData(); + + bool isEmpty(); + string valueAt(unsigned int row, unsigned int col); + int getReturnCode(); + string getOriginalQuery(); + unsigned int columnCount(); + unsigned int rowCount(); + private: + vector> values; + int queryCode; + string originalQuery; + + //Utility methods. + string convertToString(sqlite3_value* val); + bool isIndexValid(unsigned int row, unsigned int col); +}; + +#endif // RESULTTABLE_H 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. 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.cpp b/utils/fileutils.cpp new file mode 100644 index 0000000..a453c57 --- /dev/null +++ b/utils/fileutils.cpp @@ -0,0 +1,64 @@ +#include "fileutils.h" + +namespace FileUtils{ + +void ensureAppDataFolderExists(){ + QDir folder(appDataPath); + if (!folder.exists()){ + folder.mkpath("."); + } +} + +bool saveInstruction(int nr, Instruction instruction){ + ensureAppDataFolderExists(); + QString filename = appDataPath + QString::fromStdString(std::to_string(nr)) +".html"; + QFile file(filename); + if (file.open(QIODevice::WriteOnly)){ + QTextStream stream(&file); + stream< +#include +#include +#include +#include + +#include "model/recipe/instruction.h" + +namespace FileUtils{ + + const QString appDataPath = QStandardPaths::writableLocation(QStandardPaths::HomeLocation)+"/.recipeDB/"; + + void ensureAppDataFolderExists(); + + bool saveInstruction(int nr, Instruction instruction); + + Instruction loadInstruction(int nr); + + bool saveImage(int nr, QImage image); + + QImage loadImage(int nr); +} + +#endif // FILEUTILS_H