Shift to master. Not done but database abstraction is nearly done. #3

Merged
andrewlalis merged 10 commits from development into master 2018-03-03 09:20:11 +00:00
10 changed files with 109 additions and 59 deletions
Showing only changes of commit 8c0163951b - Show all commits

View File

@ -39,7 +39,8 @@ HEADERS += model/recipe/instruction.h \
SQLite/sqlite3ext.h \ SQLite/sqlite3ext.h \
model/database/resulttable.h \ model/database/resulttable.h \
model/database/recipedatabase.h \ model/database/recipedatabase.h \
utils/fileutils.h utils/fileutils.h \
utils/stringutils.h
LIBS += -ldl \ LIBS += -ldl \

View File

@ -12,18 +12,23 @@ int main(int argc, char *argv[])
w.show(); w.show();
//TESTING CODE //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"); RecipeDatabase recipeDB("recipes");
recipeDB.storeIngredient(Ingredient("Apple", "Fruit")); recipeDB.storeIngredient(Ingredient("Apple", "Fruit"));
recipeDB.storeIngredient(Ingredient("Corn", "Vegetable")); recipeDB.storeIngredient(Ingredient("Corn", "Vegetable"));
recipeDB.storeIngredient(Ingredient("Lettuce", "Vegetable"));
recipeDB.storeIngredient(Ingredient("Carrot", "Vegetable"));
recipeDB.executeSQL("SELECT * FROM ingredient;").printData(); recipeDB.executeSQL("SELECT * FROM ingredient;").printData();
FileUtils::saveInstruction(1, Instruction("This is some plain text with no HTML markup.")); //TESTING CODE
vector<RecipeIngredient> 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("<b>BOLD</b><i>iTaLiCs</i>"), QImage(), vector<RecipeTag>(), QDate::currentDate(), QTime(0, 30), QTime(0, 25), 10.0f);
w.loadFromRecipe(rec);
return a.exec(); return a.exec();
} }

View File

@ -15,7 +15,7 @@ ResultTable Database::executeSQL(string statement){
this->returnCode = sqlite3_prepare_v2(this->db, statement.c_str(), -1, &stmt, NULL); this->returnCode = sqlite3_prepare_v2(this->db, statement.c_str(), -1, &stmt, NULL);
ResultTable t; ResultTable t;
if (this->returnCode != SQLITE_OK){ 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; return t;
} }
@ -26,6 +26,19 @@ ResultTable Database::executeSQL(string statement){
return t; return t;
} }
bool Database::insertInto(string tableName, vector<string> columnNames, vector<string> 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(){ void Database::openConnection(){
this->returnCode = sqlite3_open(this->filename.c_str(), &this->db); this->returnCode = sqlite3_open(this->filename.c_str(), &this->db);
if (this->returnCode || this->db == NULL){ if (this->returnCode || this->db == NULL){
@ -42,6 +55,17 @@ void Database::closeConnection(){
this->dbIsOpen = false; this->dbIsOpen = false;
} }
string Database::combineVector(std::vector<string> strings, string mid){
if (strings.empty()){
return "";
}
std::string result = "'" + strings[0] + "'";
for (std::vector<std::string>::iterator it = strings.begin() + 1; it != strings.end(); ++it){
result += mid + "'" + (*it) + "'";
}
return result;
}
bool Database::tableExists(string tableName){ bool Database::tableExists(string tableName){
if (tableName.empty() || this->db == NULL || !this->dbIsOpen){ if (tableName.empty() || this->db == NULL || !this->dbIsOpen){
return false; return false;
@ -49,3 +73,7 @@ bool Database::tableExists(string tableName){
ResultTable t = executeSQL("SELECT name FROM sqlite_master WHERE type='table' AND name='"+tableName+"';"); ResultTable t = executeSQL("SELECT name FROM sqlite_master WHERE type='table' AND name='"+tableName+"';");
return !t.isEmpty(); return !t.isEmpty();
} }
int Database::getLastInsertedRowId(){
return sqlite3_last_insert_rowid(this->db);
}

View File

@ -3,6 +3,7 @@
#include <string> #include <string>
#include <vector> #include <vector>
#include <numeric>
#include "SQLite/sqlite3.h" #include "SQLite/sqlite3.h"
#include "model/recipe/ingredients/ingredient.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. //Executes an SQL string statement in a safe way and returns the result.
ResultTable executeSQL(string statement); ResultTable executeSQL(string statement);
bool insertInto(string tableName, vector<string> columnNames, vector<string> values);
bool tableExists(string tableName); bool tableExists(string tableName);
int getLastInsertedRowId();
private: private:
//SQL Instance variables. //SQL Instance variables.
string filename; string filename;
@ -36,6 +39,7 @@ private:
void openConnection(); void openConnection();
void closeConnection(); void closeConnection();
std::string combineVector(std::vector<std::string> strings, std::string mid);
}; };
#endif // DATABASE_H #endif // DATABASE_H

View File

@ -6,8 +6,44 @@ RecipeDatabase::RecipeDatabase(string filename) : Database(filename){
void RecipeDatabase::storeRecipe(Recipe recipe){ void RecipeDatabase::storeRecipe(Recipe recipe){
///TODO: Implement this in a smart way using transaction. ///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<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){
//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<string>({"foodGroup", "name"}), vector<string>({ingredient.getFoodGroup(), ingredient.getName()}));
}
}
void RecipeDatabase::ensureTablesExist(){ void RecipeDatabase::ensureTablesExist(){
//Make sure that foreign keys are enabled. //Make sure that foreign keys are enabled.
@ -17,24 +53,14 @@ void RecipeDatabase::ensureTablesExist(){
"ingredientId INTEGER PRIMARY KEY," "ingredientId INTEGER PRIMARY KEY,"
"foodGroup varchar," "foodGroup varchar,"
"name varchar);"); "name varchar);");
//Images table. //Recipe table. Each recipe can have at most one instruction, and one image.
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.
this->executeSQL("CREATE TABLE IF NOT EXISTS recipe(" this->executeSQL("CREATE TABLE IF NOT EXISTS recipe("
"recipeId INTEGER PRIMARY KEY," "recipeId INTEGER PRIMARY KEY,"
"date date," "createdDate date,"
"name varchar," "name varchar,"
"imageNr int,"
"cookTime time," "cookTime time,"
"prepTime time," "prepTime time,"
"servingCount real," "servingCount real);");
"FOREIGN KEY (imageNr) REFERENCES image(imageNr));");
//Recipe tags table. //Recipe tags table.
this->executeSQL("CREATE TABLE IF NOT EXISTS recipeTag(" this->executeSQL("CREATE TABLE IF NOT EXISTS recipeTag("
"recipeId INTEGER PRIMARY KEY," "recipeId INTEGER PRIMARY KEY,"
@ -49,27 +75,4 @@ void RecipeDatabase::ensureTablesExist(){
"comment varchar," "comment varchar,"
"FOREIGN KEY (ingredientId) REFERENCES ingredient(ingredientId)," "FOREIGN KEY (ingredientId) REFERENCES ingredient(ingredientId),"
"FOREIGN KEY (recipeId) REFERENCES recipe(recipeId));"); "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() +"');");
}
} }

View File

@ -21,8 +21,7 @@ class RecipeDatabase : public Database
void storeRecipe(Recipe recipe); void storeRecipe(Recipe recipe);
//SQL Helper methods. //SQL Helper methods.
void storeInstruction(Instruction instruction); void storeRecipeIngredient(RecipeIngredient ri);
void storeImage(QImage image);
void storeIngredient(Ingredient ingredient); void storeIngredient(Ingredient ingredient);
private: private:

View File

@ -25,10 +25,14 @@ void ResultTable::processRow(sqlite3_stmt *stmt){
} }
void ResultTable::printData(){ void ResultTable::printData(){
if (this->isEmpty()){
printf("Result table is empty.\n");
return;
}
printf("Printing table: %d by %d\n", this->rowCount(), this->columnCount()); printf("Printing table: %d by %d\n", this->rowCount(), this->columnCount());
for (unsigned int row = 0; row < this->rowCount(); row++){ for (unsigned int row = 0; row < this->rowCount(); row++){
for (unsigned int col = 0; col < this->columnCount(); col++){ 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"); printf("\n");
} }

View File

@ -7,15 +7,6 @@ MainWindow::MainWindow(QWidget *parent) :
ui->setupUi(this); ui->setupUi(this);
ui->ingredientsListView->setModel(&this->ingredientModel); ui->ingredientsListView->setModel(&this->ingredientModel);
//TESTING CODE
vector<RecipeIngredient> 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("<b>BOLD</b><i>iTaLiCs</i>"), QImage(), vector<RecipeTag>(), QDate::currentDate(), QTime(0, 30), QTime(0, 25), 10.0f);
this->loadFromRecipe(rec);
} }
MainWindow::~MainWindow(){ MainWindow::~MainWindow(){

View File

@ -19,7 +19,7 @@ namespace FileUtils {
} }
} }
void saveInstruction(int nr, Instruction instruction){ string saveInstruction(int nr, Instruction instruction){
ensureAppDataFolderExists(); ensureAppDataFolderExists();
QString filename = appDataPath + QString::fromStdString(std::to_string(nr)) +".html"; QString filename = appDataPath + QString::fromStdString(std::to_string(nr)) +".html";
QFile file(filename); QFile file(filename);
@ -27,8 +27,10 @@ namespace FileUtils {
QTextStream stream(&file); QTextStream stream(&file);
stream<<instruction.getHTML().c_str()<<endl; stream<<instruction.getHTML().c_str()<<endl;
file.close(); file.close();
return filename.toStdString();
} else { } else {
fprintf(stderr, "Error opening file: %s to write instruction.\n", filename.toStdString().c_str()); fprintf(stderr, "Error opening file: %s to write instruction.\n", filename.toStdString().c_str());
return "";
} }
} }

13
utils/stringutils.h Normal file
View File

@ -0,0 +1,13 @@
#ifndef STRINGUTILS_H
#define STRINGUTILS_H
#include <string>
#include <vector>
namespace StringUtils{
}
#endif // STRINGUTILS_H