Shift to master. Not done but database abstraction is nearly done. #3
|
@ -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 \
|
||||||
|
|
||||||
|
|
17
main.cpp
17
main.cpp
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -6,7 +6,43 @@ 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(){
|
||||||
|
@ -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() +"');");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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:
|
||||||
|
|
||||||
|
|
|
@ -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");
|
||||||
}
|
}
|
||||||
|
|
|
@ -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(){
|
||||||
|
|
|
@ -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 "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,13 @@
|
||||||
|
#ifndef STRINGUTILS_H
|
||||||
|
#define STRINGUTILS_H
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace StringUtils{
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // STRINGUTILS_H
|
Loading…
Reference in New Issue