diff --git a/RecipeDB.pro b/RecipeDB.pro
index 2c957a5..0071690 100644
--- a/RecipeDB.pro
+++ b/RecipeDB.pro
@@ -25,7 +25,9 @@ SOURCES += model/recipe/instruction.cpp \
SQLite/sqlite3.c \
model/database/resulttable.cpp \
model/database/recipedatabase.cpp \
- utils/fileutils.cpp
+ utils/fileutils.cpp \
+ gui/newrecipedialog.cpp \
+ model/recipe/tags/taglistmodel.cpp
HEADERS += model/recipe/instruction.h \
model/recipe/recipe.h \
@@ -40,11 +42,14 @@ HEADERS += model/recipe/instruction.h \
SQLite/sqlite3ext.h \
model/database/resulttable.h \
model/database/recipedatabase.h \
- utils/fileutils.h
+ utils/fileutils.h \
+ gui/newrecipedialog.h \
+ model/recipe/tags/taglistmodel.h
LIBS += -ldl \
-FORMS += gui/mainwindow.ui
+FORMS += gui/mainwindow.ui \
+ gui/newrecipedialog.ui
DISTFILES += \
.gitignore
diff --git a/gui/mainwindow.ui b/gui/mainwindow.ui
index 1db1cc9..ae24fae 100644
--- a/gui/mainwindow.ui
+++ b/gui/mainwindow.ui
@@ -109,12 +109,25 @@
- Source Sans Pro Light
+ Noto Sans CJK KR Light
20
+ PreferAntialias
+
+ false
+
- background-color: rgb(83, 75, 255);
+ QPushButton#newButton {
+ background-color: rgb(235, 235, 255);
+ border: 0px;
+}
+QPushButton#newButton:hover{
+ background-color: rgb(245, 245, 255);
+}
+QPushButton#newButton:pressed{
+ background-color: rgb(255, 255, 255);
+}
New
@@ -134,12 +147,22 @@
- Source Sans Pro Light
+ Noto Sans CJK KR Light
20
+ PreferAntialias
- background-color: rgb(112, 105, 255);
+ QPushButton#openButton {
+ background-color: rgb(222, 226, 255);
+ border: 0px;
+}
+QPushButton#openButton:hover{
+ background-color: rgb(232, 236, 255);
+}
+QPushButton#openButton:pressed{
+ background-color: rgb(255, 255, 255);
+}
Open
@@ -156,16 +179,32 @@
- Source Sans Pro Light
+ Noto Sans CJK KR Light
20
+ PreferAntialias
+
+ false
+
- background-color: rgb(137, 131, 255);
+ QPushButton#browseButton {
+ background-color: rgb(215, 215, 255);
+ border: 0px;
+}
+QPushButton#browseButton:hover{
+ background-color: rgb(225, 225, 255);
+}
+QPushButton#browseButton:pressed{
+ background-color: rgb(255, 255, 255);
+}
Browse
+
+ false
+
@@ -214,8 +253,13 @@
- Source Sans Pro Light
+ Noto Sans CJK KR Light
24
+ 50
+ false
+ false
+ PreferAntialias
+ true
@@ -299,7 +343,7 @@
- Source Sans Pro Light
+ Noto Sans CJK KR Thin
18
@@ -324,8 +368,9 @@
- Source Sans Pro Light
- 16
+ Noto Sans CJK KR Light
+ 12
+ PreferAntialias
@@ -343,6 +388,9 @@
Qt::ScrollBarAlwaysOff
+
+ QAbstractItemView::NoSelection
+
true
@@ -397,7 +445,7 @@
- Source Sans Pro Light
+ Noto Sans CJK KR Thin
18
@@ -414,6 +462,12 @@
-
+
+
+ Noto Sans CJK KR Medium
+ PreferAntialias
+
+
background-color: rgb(244, 244, 244);
@@ -445,11 +499,11 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
<html><head><meta name="qrichtext" content="1" /><style type="text/css">
p, li { white-space: pre-wrap; }
-</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;">
-<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" color:#00ff40;">This is some </span><span style=" font-size:16pt; color:#a33c3e;">colored text and </span><a href="https://www.google.com"><span style=" font-size:8pt; text-decoration: underline; color:#0000ff;">link</span></a></p></body></html>
+</style></head><body style=" font-family:'Noto Sans CJK KR Medium'; font-size:11pt; font-weight:400; font-style:normal;">
+<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'MS Shell Dlg 2'; font-size:8.25pt; color:#00ff40;">This is some </span><span style=" font-family:'MS Shell Dlg 2'; font-size:16pt; color:#a33c3e;">colored text and </span><a href="https://www.google.com"><span style=" font-family:'MS Shell Dlg 2'; font-size:8pt; text-decoration: underline; color:#0000ff;">link</span></a></p></body></html>
- Qt::LinksAccessibleByMouse
+ Qt::TextSelectableByMouse
diff --git a/gui/newrecipedialog.cpp b/gui/newrecipedialog.cpp
new file mode 100644
index 0000000..7405d09
--- /dev/null
+++ b/gui/newrecipedialog.cpp
@@ -0,0 +1,103 @@
+#include "newrecipedialog.h"
+#include "ui_newrecipedialog.h"
+
+NewRecipeDialog::NewRecipeDialog(QWidget *parent) :
+ QDialog(parent),
+ ui(new Ui::NewRecipeDialog){
+ ui->setupUi(this);
+
+ setModal(true);
+
+ ui->ingredientsListView->setModel(&this->ingredientListModel);
+ ui->tagsListView->setModel(&this->tagsListModel);
+}
+
+NewRecipeDialog::NewRecipeDialog(RecipeDatabase *db, QWidget *parent) : NewRecipeDialog(parent){
+ this->recipeDB = db;
+
+
+ this->populateIngredientsBox();
+ this->populateUnitsBox();
+ this->populateTagsBox();
+}
+
+NewRecipeDialog::~NewRecipeDialog(){
+ delete ui;
+}
+
+Recipe NewRecipeDialog::getRecipe(){
+ Recipe r(ui->recipeNameEdit->text().toStdString(),
+ this->ingredientListModel.getIngredients(),
+ ui->instructionsTextEdit->toHtml().toStdString(),
+ QImage(),//Image
+ this->tagsListModel.getTags(),//Tags
+ QDate::currentDate(),
+ ui->prepTimeEdit->time(),
+ ui->cookTimeEdit->time(),
+ (float)ui->servingsSpinBox->value());
+}
+
+bool NewRecipeDialog::isAccepted() const{
+ return this->accepted;
+}
+
+void NewRecipeDialog::populateIngredientsBox(){
+ this->ingredients = this->recipeDB->retrieveAllIngredients();
+ ui->ingredientNameBox->clear();
+ for (unsigned int i = 0; i < this->ingredients.size(); i++){
+ QString s = QString::fromStdString(this->ingredients[i].getName());
+ ui->ingredientNameBox->insertItem(i, s);
+ }
+}
+
+void NewRecipeDialog::populateUnitsBox(){
+ this->units = this->recipeDB->retrieveAllUnitsOfMeasure();
+ ui->unitComboBox->clear();
+ for (unsigned int i = 0; i < this->units.size(); i++){
+ QString s = QString::fromStdString(this->units[i].getName());
+ ui->unitComboBox->insertItem(i, s);
+ }
+}
+
+void NewRecipeDialog::populateTagsBox(){
+ this->tags = this->recipeDB->retrieveAllTags();
+ ui->tagsComboBox->clear();
+ for (unsigned int i = 0; i < this->tags.size(); i++){
+ QString s = QString::fromStdString(this->tags[i].getValue());
+ ui->tagsComboBox->insertItem(i, s);
+ }
+}
+
+void NewRecipeDialog::on_addIngredientButton_clicked(){
+ //Construct a recipe ingredient from the supplied data.
+ Ingredient i = this->ingredients[ui->ingredientNameBox->currentIndex()];
+ UnitOfMeasure u = this->units[ui->unitComboBox->currentIndex()];
+ RecipeIngredient ri(i, ui->quantitySpinBox->value(), u, ui->commentsLineEdit->text().toStdString());
+ this->ingredientListModel.addIngredient(ri);
+}
+
+void NewRecipeDialog::on_italicsButton_clicked(){
+ ui->instructionsTextEdit->setFontItalic(ui->italicsButton->isChecked());
+}
+
+void NewRecipeDialog::on_boldButton_clicked(){
+ if (ui->boldButton->isChecked()){
+ ui->instructionsTextEdit->setFontWeight(QFont::Bold);
+ } else {
+ ui->instructionsTextEdit->setFontWeight(QFont::Normal);
+ }
+}
+
+void NewRecipeDialog::on_buttonBox_accepted(){
+ this->accepted = true;
+ this->close();
+}
+
+void NewRecipeDialog::on_buttonBox_rejected(){
+ this->close();
+}
+
+void NewRecipeDialog::on_addTagButton_clicked(){
+ //Add a tag to the list of those prepared to be added.
+ this->tagsListModel.addTag(this->tags[ui->tagsComboBox->currentIndex()]);
+}
diff --git a/gui/newrecipedialog.h b/gui/newrecipedialog.h
new file mode 100644
index 0000000..47cd848
--- /dev/null
+++ b/gui/newrecipedialog.h
@@ -0,0 +1,55 @@
+#ifndef NEWRECIPEDIALOG_H
+#define NEWRECIPEDIALOG_H
+
+#include
+#include
+
+#include "model/database/recipedatabase.h"
+#include "model/recipe/ingredients/ingredientlistmodel.h"
+#include "model/recipe/tags/taglistmodel.h"
+
+namespace Ui {
+class NewRecipeDialog;
+}
+
+class NewRecipeDialog : public QDialog
+{
+ Q_OBJECT
+
+ public:
+ explicit NewRecipeDialog(QWidget *parent = 0);
+ NewRecipeDialog(RecipeDatabase *db, QWidget *parent = 0);
+ ~NewRecipeDialog();
+
+ Recipe getRecipe();
+ bool isAccepted() const;
+ private slots:
+ void on_addIngredientButton_clicked();
+
+ void on_italicsButton_clicked();
+
+ void on_boldButton_clicked();
+
+ void on_buttonBox_accepted();
+
+ void on_buttonBox_rejected();
+
+ void on_addTagButton_clicked();
+
+ private:
+ Ui::NewRecipeDialog *ui;
+ RecipeDatabase *recipeDB;
+ vector ingredients;
+ vector units;
+ vector tags;
+ IngredientListModel ingredientListModel;
+ TagListModel tagsListModel;
+ bool accepted = false;
+
+ //Helper functions to fill fields.
+ void populateIngredientsBox();
+ void populateUnitsBox();
+ void populateTagsBox();
+};
+
+#endif // NEWRECIPEDIALOG_H
diff --git a/gui/newrecipedialog.ui b/gui/newrecipedialog.ui
new file mode 100644
index 0000000..78e7f57
--- /dev/null
+++ b/gui/newrecipedialog.ui
@@ -0,0 +1,685 @@
+
+
+ NewRecipeDialog
+
+
+ true
+
+
+
+ 0
+ 0
+ 640
+ 689
+
+
+
+
+ 0
+ 0
+
+
+
+ New Recipe
+
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+ -
+
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
-
+
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
-
+
+
+ Qt::LeftToRight
+
+
+ background-color: rgb(234, 235, 255);
+
+
+
-
+
+
+
-
+
+
+
+ Noto Sans CJK KR
+ 14
+ 50
+ false
+ false
+
+
+
+ Edit Recipe
+
+
+ Qt::AlignCenter
+
+
+
+ -
+
+
+
+ PreferAntialias
+
+
+
+ Qt::AlignCenter
+
+
+ Recipe Name
+
+
+
+
+
+
+ -
+
+
+
-
+
+
+
-
+
+
+ Prep Time
+
+
+ Qt::AlignCenter
+
+
+
+ -
+
+
+
+ 0
+ 0
+ 0
+ 1999
+ 12
+ 27
+
+
+
+ QDateTimeEdit::HourSection
+
+
+ hh:mm:ss
+
+
+ Qt::UTC
+
+
+
+
+
+
+
+
+
+ -
+
+
+
-
+
+
+ Cook Time
+
+
+ Qt::AlignCenter
+
+
+
+ -
+
+
+ QDateTimeEdit::HourSection
+
+
+ hh:mm:ss
+
+
+ Qt::UTC
+
+
+
+
+
+
+
+
+
+ -
+
+
+
-
+
+
+ Servings
+
+
+ Qt::AlignCenter
+
+
+
+ -
+
+
+ 1
+
+
+ 1.000000000000000
+
+
+
+
+
+
+
+
+
+
+
+
+ -
+
+
+ background-color: rgb(222, 226, 255);
+
+
+
-
+
+
+ Tags
+
+
+ Qt::AlignCenter
+
+
+
+ -
+
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
-
+
+
+ -
+
+
+ New
+
+
+
+
+
+
+ -
+
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
-
+
+
+ Add
+
+
+
+ -
+
+
+ Delete
+
+
+
+
+
+
+ -
+
+
+ false
+
+
+ background-color: rgb(255, 255, 255);
+
+
+ QFrame::NoFrame
+
+
+
+
+
+
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ background-color: rgb(245, 245, 255);
+
+
+
+ 0
+
+
+ QLayout::SetMaximumSize
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
-
+
+
+ Ingredients
+
+
+ Qt::AlignCenter
+
+
+
+ -
+
+
+
-
+
+
+ background-color: rgb(255, 255, 255);
+
+
+ QFrame::NoFrame
+
+
+ 100
+
+
+
+ -
+
+
+
+ 5
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
-
+
+
+ Add Ingredient
+
+
+ Qt::AlignCenter
+
+
+
+ -
+
+
+
+ 2
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
-
+
+
+
+ 0
+ 0
+
+
+
+
+
+
+
+ -
+
+
+ New
+
+
+
+
+
+
+ -
+
+
+
+ 0
+ 36
+
+
+
+
+ 2
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
-
+
+
+
+ Noto Sans CJK KR
+ 14
+ 50
+ false
+ false
+
+
+
+ Amount
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ 10000.000000000000000
+
+
+ 1.000000000000000
+
+
+
+ -
+
+
+ -
+
+
+ New
+
+
+
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ false
+
+
+ Qt::AlignCenter
+
+
+ Comments
+
+
+ false
+
+
+
+ -
+
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
-
+
+
+ Add
+
+
+
+ -
+
+
+ Delete
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ -
+
+
+
-
+
+
+ Instructions
+
+
+ Qt::AlignCenter
+
+
+
+ -
+
+
+
-
+
+
+
+ Liberation Serif
+ 12
+ true
+
+
+
+ I
+
+
+ true
+
+
+ Qt::ToolButtonIconOnly
+
+
+
+ -
+
+
+
+ Liberation Serif
+ 12
+ 75
+ true
+
+
+
+ B
+
+
+ true
+
+
+ false
+
+
+ false
+
+
+
+
+
+
+ -
+
+
+ Enter instructions here.
+
+
+
+
+
+
+ -
+
+
+ Qt::LeftToRight
+
+
+ QDialogButtonBox::Cancel|QDialogButtonBox::Ok
+
+
+
+
+
+
+
+
+
+
+
diff --git a/main.cpp b/main.cpp
index d67a414..23f8db2 100644
--- a/main.cpp
+++ b/main.cpp
@@ -1,4 +1,5 @@
#include "userInterface/mainwindow.h"
+#include "gui/newrecipedialog.h"
#include
#include "model/database/database.h"
@@ -13,27 +14,42 @@ int main(int argc, char *argv[])
//TESTING CODE
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")));
+ ri.push_back(RecipeIngredient("flour", "grains", 3.0f, UnitOfMeasure("cup", "cups", "c", UnitOfMeasure::VOLUME, 1.0), ""));
+ ri.push_back(RecipeIngredient("baking powder", "additives", 1.0f, UnitOfMeasure("teaspoon", "teaspoons", "tsp", UnitOfMeasure::VOLUME, 1.0), ""));
- Recipe rec("Example", ri, Instruction("BOLDiTaLiCs"), QImage(), vector(), QDate::currentDate(), QTime(0, 30), QTime(0, 25), 10.0f);
+ Recipe rec("Example",
+ ri,
+ Instruction("BOLDiTaLiCs"),
+ QImage(),
+ vector({RecipeTag("testing"),
+ RecipeTag("fake")}),
+ QDate::currentDate(),
+ QTime(0, 30),
+ QTime(0, 25),
+ 10.0f);
bool success = recipeDB.storeRecipe(rec);
printf("Storage successful: %d\n", success);
+ recipeDB.storeUnitOfMeasure(UnitOfMeasure("tablespoon", "tablespoons", "tbsp", UnitOfMeasure::VOLUME, 1.0));
+ recipeDB.storeUnitOfMeasure(UnitOfMeasure("pinch", "pinches", "pch", UnitOfMeasure::VOLUME, 1.0));
+ recipeDB.storeUnitOfMeasure(UnitOfMeasure("gram", "grams", "g", UnitOfMeasure::MASS, 1.0));
+
Recipe reloadRec = recipeDB.retrieveRecipe("Example");
reloadRec.print();
w.loadFromRecipe(reloadRec);
+ NewRecipeDialog d(&recipeDB);
+ d.show();
+ d.exec();
+
+ if (d.isAccepted()){
+ printf("Accepted the dialog.\n");
+ }
+
return a.exec();
}
diff --git a/model/database/database.cpp b/model/database/database.cpp
index 6fb4394..636a137 100644
--- a/model/database/database.cpp
+++ b/model/database/database.cpp
@@ -42,7 +42,7 @@ ResultTable Database::selectFrom(string tableName, string columnNames, string co
if (columnNames.size() == 0 || tableName.empty()){
return ResultTable();
}
- string query = "SELECT " + columnNames + " FROM " + tableName + " WHERE " + conditions + ";";
+ string query = "SELECT " + columnNames + " FROM " + tableName + " " + conditions + ";";
return this->executeSQL(query);
}
@@ -81,8 +81,7 @@ bool Database::tableExists(string tableName){
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+"';");
+ ResultTable t = this->selectFrom("sqlite_master", "name", "WHERE type='table' AND name='"+tableName+"'");
return !t.isEmpty();
}
diff --git a/model/database/recipedatabase.cpp b/model/database/recipedatabase.cpp
index 4c991da..b64a660 100644
--- a/model/database/recipedatabase.cpp
+++ b/model/database/recipedatabase.cpp
@@ -5,9 +5,9 @@ RecipeDatabase::RecipeDatabase(string filename) : Database(filename){
}
bool RecipeDatabase::storeRecipe(Recipe recipe){
- ///TODO: Implement this in a smart way using transaction.
+ //Store a recipe, if it doesn't already exist. This first tries to create the recipe entry, then all subsequent supporting table entries.
this->executeSQL("BEGIN;");
- ResultTable t = this->selectFrom("recipe", "*", "name="+surroundString(recipe.getName(), "'"));
+ ResultTable t = this->selectFrom("recipe", "*", "WHERE name="+surroundString(recipe.getName(), "'"));
if (!t.isEmpty()){
fprintf(stderr, "Error storing recipe: Recipe with name %s already exists.\n", recipe.getName().c_str());
} else {
@@ -50,17 +50,11 @@ bool RecipeDatabase::storeRecipe(Recipe recipe){
}
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));
- }
+ int ingId = this->storeIngredient(ri);
+ if (ingId < 0) return false;
+
+ if (!this->storeUnitOfMeasure(ri.getUnit())) return false;
+
return this->insertInto("recipeIngredient",
vector({
"ingredientId",
@@ -78,13 +72,43 @@ bool RecipeDatabase::storeRecipeIngredient(RecipeIngredient ri, int recipeId){
}));
}
-void RecipeDatabase::storeIngredient(Ingredient ingredient){
- ResultTable t = this->selectFrom("ingredient", "*", "name="+surroundString(ingredient.getName(), "'"));
+int RecipeDatabase::storeIngredient(Ingredient ingredient){
+ ResultTable t = this->selectFrom("ingredient", "*", "WHERE name="+surroundString(ingredient.getName(), "'"));
if (t.isEmpty()){
- this->insertInto("ingredient", vector({"foodGroup", "name"}), vector({ingredient.getFoodGroup(), ingredient.getName()}));
+ bool success = this->insertInto("ingredient", vector({"foodGroup", "name"}), vector({ingredient.getFoodGroup(), ingredient.getName()}));
+ if (success){
+ return this->getLastInsertedRowId();
+ } else {
+ return -1;
+ }
+ } else {
+ return std::stoi(t.valueAt(0, 0));
}
}
+bool RecipeDatabase::storeUnitOfMeasure(UnitOfMeasure u){
+ ResultTable t = this->selectFrom("unitOfMeasure", "name", "WHERE name="+surroundString(u.getName(), "'"));
+ if (!t.isEmpty()){
+ return true;
+ }
+ bool success = this->insertInto("unitOfMeasure",
+ vector({
+ "name",
+ "plural",
+ "abbreviation",
+ "type",
+ "metricCoefficient"
+ }),
+ vector({
+ u.getName(),
+ u.getNamePlural(),
+ u.getAbbreviation(),
+ std::to_string(u.getType()),
+ std::to_string(u.getMetricCoefficient())
+ }));
+ return success;
+}
+
bool RecipeDatabase::storeInstruction(Instruction instruction, int recipeId){
return FileUtils::saveInstruction(recipeId, instruction);
}
@@ -112,12 +136,11 @@ bool RecipeDatabase::storeTags(vector tags, int recipeId){
}
Recipe RecipeDatabase::retrieveRecipe(string name){
- ResultTable t = this->selectFrom("recipe", "*", "name="+surroundString(name, "'"));
+ ResultTable t = this->selectFrom("recipe", "*", "WHERE 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));
@@ -128,24 +151,78 @@ Recipe RecipeDatabase::retrieveRecipe(string name){
r.setInstruction(FileUtils::loadInstruction(id));
r.setImage(FileUtils::loadImage(id));
r.setIngredients(this->retrieveRecipeIngredients(id));
+ r.setTags(this->retrieveTags(id));
return r;
}
vector RecipeDatabase::retrieveRecipeIngredients(int recipeId){
- ResultTable t = this->executeSQL("SELECT ingredient.name, ingredient.foodGroup, recipeIngredient.quantity, recipeIngredient.unitName, recipeIngredient.comment "
+ ResultTable t = this->executeSQL("SELECT ingredient.name, ingredient.foodGroup, "//0, 1
+ "recipeIngredient.quantity, recipeIngredient.unitName, recipeIngredient.comment,"//2, 3, 4
+ "unitOfMeasure.name, unitOfMeasure.plural, unitOfMeasure.abbreviation, unitOfMeasure.type, unitOfMeasure.metricCoefficient "//5, 6, 7, 8, 9
"FROM ingredient "
"INNER JOIN recipeIngredient "
"ON ingredient.ingredientId = recipeIngredient.ingredientId "
- "AND recipeIngredient.recipeId = "+std::to_string(recipeId)+";");
- t.printData();
+ "INNER JOIN unitOfMeasure "
+ "ON recipeIngredient.unitName = unitOfMeasure.name "
+ "WHERE recipeIngredient.recipeId = "+std::to_string(recipeId)+";");
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)));
+ RecipeIngredient r(t.valueAt(row, 0),
+ t.valueAt(row, 1),
+ std::stof(t.valueAt(row, 2)),
+ UnitOfMeasure(t.valueAt(row, 5), t.valueAt(row, 6), t.valueAt(row, 7), std::stoi(t.valueAt(row, 8)), std::stod(t.valueAt(row, 9))),
+ t.valueAt(row, 4));
ings.push_back(r);
}
return ings;
}
+vector RecipeDatabase::retrieveAllIngredients(){
+ ResultTable t = this->selectFrom("ingredient", "*", "ORDER BY name");
+ vector ings;
+ for (unsigned int row = 0; row < t.rowCount(); row++){
+ Ingredient i(t.valueAt(row, 2), t.valueAt(row, 1));
+ ings.push_back(i);
+ }
+ return ings;
+}
+
+vector RecipeDatabase::retrieveAllUnitsOfMeasure(){
+ ResultTable t = this->selectFrom("unitOfMeasure", "*", "ORDER BY name");
+ vector units;
+ if (!t.isEmpty()){
+ for (unsigned int row = 0; row < t.rowCount(); row++){
+ UnitOfMeasure u(t.valueAt(row, 0), t.valueAt(row, 1), t.valueAt(row, 2), std::stoi(t.valueAt(row, 3)), std::stod(t.valueAt(row, 4)));
+ units.push_back(u);
+ }
+ }
+ return units;
+}
+
+vector RecipeDatabase::retrieveTags(int recipeId){
+ ResultTable t = this->selectFrom("recipeTag", "tagName", "WHERE recipeId="+std::to_string(recipeId));
+ vector tags;
+ if (!t.isEmpty()){
+ for (unsigned int row = 0; row < t.rowCount(); row++){
+ RecipeTag tag(t.valueAt(row, 0));
+ tags.push_back(tag);
+ }
+ }
+ return tags;
+}
+
+vector RecipeDatabase::retrieveAllTags(){
+ ResultTable t = this->selectFrom("recipeTag", "tagName", "ORDER BY tagName");
+ vector tags;
+ if (!t.isEmpty()){
+ for (unsigned int row = 0; row < t.rowCount(); row++){
+ RecipeTag tag(t.valueAt(row, 0));
+ tags.push_back(tag);
+ }
+ }
+ return tags;
+}
+
void RecipeDatabase::ensureTablesExist(){
//Make sure that foreign keys are enabled.
this->executeSQL("PRAGMA foreign_keys = ON;");
@@ -156,6 +233,13 @@ void RecipeDatabase::ensureTablesExist(){
"ingredientId INTEGER PRIMARY KEY,"
"foodGroup varchar,"
"name varchar UNIQUE);");
+ //Unit of Measure table.
+ this->executeSQL("CREATE TABLE IF NOT EXISTS unitOfMeasure("
+ "name varchar UNIQUE PRIMARY KEY,"
+ "plural varchar,"
+ "abbreviation varchar,"
+ "type int,"
+ "metricCoefficient real);");
//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,"
@@ -177,6 +261,7 @@ void RecipeDatabase::ensureTablesExist(){
"unitName varchar,"
"comment varchar,"
"FOREIGN KEY (ingredientId) REFERENCES ingredient(ingredientId),"
- "FOREIGN KEY (recipeId) REFERENCES recipe(recipeId));");
+ "FOREIGN KEY (recipeId) REFERENCES recipe(recipeId),"
+ "FOREIGN KEY (unitName) REFERENCES unitOfMeasure(name));");
this->executeSQL("COMMIT;");
}
diff --git a/model/database/recipedatabase.h b/model/database/recipedatabase.h
index e91b45d..f1d9867 100644
--- a/model/database/recipedatabase.h
+++ b/model/database/recipedatabase.h
@@ -22,14 +22,21 @@ class RecipeDatabase : public Database
bool storeRecipe(Recipe recipe);
//SQL Helper methods.
+ //Storage.
bool storeRecipeIngredient(RecipeIngredient ri, int recipeId);
- void storeIngredient(Ingredient ingredient);
+ int storeIngredient(Ingredient ingredient);
+ bool storeUnitOfMeasure(UnitOfMeasure u);
bool storeInstruction(Instruction instruction, int recipeId);
bool storeImage(QImage image, int recipeId);
bool storeTags(vector tags, int recipeId);
+ //Retrieval.
Recipe retrieveRecipe(string name);
vector retrieveRecipeIngredients(int recipeId);
+ vector retrieveAllIngredients();
+ vector retrieveAllUnitsOfMeasure();
+ vector retrieveTags(int recipeId);
+ vector retrieveAllTags();
private:
//Utility methods.
diff --git a/model/recipe/ingredients/ingredientlistmodel.cpp b/model/recipe/ingredients/ingredientlistmodel.cpp
index de88da5..1370bc7 100644
--- a/model/recipe/ingredients/ingredientlistmodel.cpp
+++ b/model/recipe/ingredients/ingredientlistmodel.cpp
@@ -10,10 +10,23 @@ int IngredientListModel::rowCount(const QModelIndex &parent) const{
QVariant IngredientListModel::data(const QModelIndex &index, int role) const{
int row = index.row();
+ RecipeIngredient i = this->ingredients[row];
+
+ string displayStr;
+
+ if (std::ceil(i.getQuantity()) == i.getQuantity()){
+ //The quantity is an integer and should be casted.
+ displayStr += std::to_string((int)i.getQuantity());
+ } else {
+ float q = i.getQuantity();
+ displayStr += toString(q);
+ }
+
+ displayStr += " " + i.getUnit().getAbbreviation() + " " + i.getName();
switch(role){
case Qt::DisplayRole:
- return QString::fromStdString(ingredients[row].getName());
+ return QString::fromStdString(displayStr);
}
return QVariant();
@@ -23,5 +36,37 @@ void IngredientListModel::setIngredients(vector ingredients){
this->ingredients = ingredients;
QModelIndex index = createIndex(0, 0);
QModelIndex bottomIndex = createIndex(ingredients.size()-1, 0);
- emit dataChanged(index, bottomIndex);
+ emit dataChanged(index, bottomIndex);
+}
+
+bool IngredientListModel::addIngredient(RecipeIngredient ri){
+ //Add only if it doesn't exist already.
+ for (unsigned int i = 0; i < this->ingredients.size(); i++){
+ if (!this->ingredients[i].getName().compare(ri.getName())){
+ return false;
+ }
+ }
+ this->ingredients.push_back(ri);
+ QModelIndex index = createIndex(this->ingredients.size()-1, 0);
+ QModelIndex bottomIndex = createIndex(this->ingredients.size()-1, 0);
+ emit dataChanged(index, bottomIndex);
+ return true;
+}
+
+vector IngredientListModel::getIngredients(){
+ return this->ingredients;
+}
+
+string toString(float val){
+ float decimal = std::fmod(val, 1.0f);
+ int places = 1;
+ while (std::fmod(decimal * 10, 1.0f) > 0){
+ decimal *= 10;
+ places++;
+ }
+ char buffer[50];
+ string arg = "%."+std::to_string(places)+"f";
+ sprintf(buffer, arg.c_str(), val);
+ string s = buffer;
+ return s;
}
diff --git a/model/recipe/ingredients/ingredientlistmodel.h b/model/recipe/ingredients/ingredientlistmodel.h
index 64ca72a..1f3605c 100644
--- a/model/recipe/ingredients/ingredientlistmodel.h
+++ b/model/recipe/ingredients/ingredientlistmodel.h
@@ -17,9 +17,16 @@ public:
//Custom methods to handle ingredient data.
void setIngredients(vector ingredients);
+ bool addIngredient(RecipeIngredient ri);
+ vector getIngredients();
private:
vector ingredients;
+
+ //Helper for printing.
+
};
+string toString(float val);
+
#endif // INGREDIENTLISTMODEL_H
diff --git a/model/recipe/ingredients/recipeingredient.cpp b/model/recipe/ingredients/recipeingredient.cpp
index c48155c..7a3b75a 100644
--- a/model/recipe/ingredients/recipeingredient.cpp
+++ b/model/recipe/ingredients/recipeingredient.cpp
@@ -1,15 +1,17 @@
#include "model/recipe/ingredients/recipeingredient.h"
-RecipeIngredient::RecipeIngredient(string name, string foodGroup, float quantity, UnitOfMeasure unit) : Ingredient(name, foodGroup){
+RecipeIngredient::RecipeIngredient(string name, string foodGroup, float quantity, UnitOfMeasure unit, string comment) : Ingredient(name, foodGroup){
setQuantity(quantity);
setUnit(unit);
+ setComment(comment);
}
-RecipeIngredient::RecipeIngredient(Ingredient i, float quantity, UnitOfMeasure unit){
+RecipeIngredient::RecipeIngredient(Ingredient i, float quantity, UnitOfMeasure unit, string comment){
setName(i.getName());
setFoodGroup(i.getFoodGroup());
setQuantity(quantity);
setUnit(unit);
+ setComment(comment);
}
RecipeIngredient::RecipeIngredient(){
diff --git a/model/recipe/ingredients/recipeingredient.h b/model/recipe/ingredients/recipeingredient.h
index 70c20f6..ae5fef9 100644
--- a/model/recipe/ingredients/recipeingredient.h
+++ b/model/recipe/ingredients/recipeingredient.h
@@ -16,9 +16,9 @@ class RecipeIngredient : public Ingredient
{
public:
//Constructor for new RecipeIngredient without starting child ingredient.
- RecipeIngredient(string name, string foodGroup, float quantity, UnitOfMeasure unit);
+ RecipeIngredient(string name, string foodGroup, float quantity, UnitOfMeasure unit, string comment);
//Constructor using data from a child ingredient.
- RecipeIngredient(Ingredient i, float quantity, UnitOfMeasure unit);
+ RecipeIngredient(Ingredient i, float quantity, UnitOfMeasure unit, string comment);
RecipeIngredient();
//Getters
diff --git a/model/recipe/ingredients/unitofmeasure.cpp b/model/recipe/ingredients/unitofmeasure.cpp
index ddf62c8..dedcb75 100644
--- a/model/recipe/ingredients/unitofmeasure.cpp
+++ b/model/recipe/ingredients/unitofmeasure.cpp
@@ -1,19 +1,23 @@
#include "unitofmeasure.h"
-UnitOfMeasure::UnitOfMeasure(string name, string plural, string abbreviation){
+UnitOfMeasure::UnitOfMeasure(string name, string plural, string abbreviation, int type, double coef){
this->name = name;
this->plural = plural;
this->abbreviation = abbreviation;
+ this->type = type;
+ this->metricCoefficient = coef;
}
UnitOfMeasure::UnitOfMeasure(string name){
this->name = name;
this->plural = name + "s";
this->abbreviation = "NULL";
+ this->type = MISC;
+ this->metricCoefficient = 1;
///TODO: Make actual guessing of this stuff.
}
-UnitOfMeasure::UnitOfMeasure() : UnitOfMeasure::UnitOfMeasure("", "", ""){
+UnitOfMeasure::UnitOfMeasure() : UnitOfMeasure::UnitOfMeasure("", "", "", MISC, 1.0){
//Default constructor initializes all fields to empty strings.
}
@@ -26,5 +30,13 @@ string UnitOfMeasure::getNamePlural() const{
}
string UnitOfMeasure::getAbbreviation() const{
- return this->abbreviation;
+ return this->abbreviation;
+}
+
+int UnitOfMeasure::getType() const{
+ return this->type;
+}
+
+double UnitOfMeasure::getMetricCoefficient() const{
+ return this->metricCoefficient;
}
diff --git a/model/recipe/ingredients/unitofmeasure.h b/model/recipe/ingredients/unitofmeasure.h
index 1787f14..b446e2e 100644
--- a/model/recipe/ingredients/unitofmeasure.h
+++ b/model/recipe/ingredients/unitofmeasure.h
@@ -12,8 +12,14 @@ using namespace std;
class UnitOfMeasure
{
public:
+ //Constants Declarations.
+ static const int MASS = 1;
+ static const int VOLUME = 2;
+ static const int LENGTH = 3;
+ static const int MISC = 4;
+
//Full constructor.
- UnitOfMeasure(string name, string plural, string abbreviation);
+ UnitOfMeasure(string name, string plural, string abbreviation, int type, double coef);
//Attempt to guess unit from just a string.
UnitOfMeasure(string name);
//Constructor with default values.
@@ -23,10 +29,14 @@ public:
string getName() const;
string getNamePlural() const;
string getAbbreviation() const;
+ int getType() const;
+ double getMetricCoefficient() const;
private:
string name; //The name of the unit of measure.
string plural; //The plural name.
string abbreviation; //A short version of the unit.
+ int type; //The type of unit, as one of the constants above.
+ double metricCoefficient; //The conversion from this unit to the standard metric unit.
};
#endif // UNITOFMEASURE_H
diff --git a/model/recipe/tags/recipetag.cpp b/model/recipe/tags/recipetag.cpp
index 22cd27d..3b1442a 100644
--- a/model/recipe/tags/recipetag.cpp
+++ b/model/recipe/tags/recipetag.cpp
@@ -8,7 +8,7 @@ RecipeTag::RecipeTag(string val){
this->value = val;
}
-string RecipeTag::getValue(){
+string RecipeTag::getValue() const{
return this->value;
}
diff --git a/model/recipe/tags/recipetag.h b/model/recipe/tags/recipetag.h
index 5613af7..cd382fe 100644
--- a/model/recipe/tags/recipetag.h
+++ b/model/recipe/tags/recipetag.h
@@ -16,7 +16,7 @@ public:
RecipeTag(string val);
//Getters
- string getValue();
+ string getValue() const;
//Setters
void setValue(string newValue);
private:
diff --git a/model/recipe/tags/taglistmodel.cpp b/model/recipe/tags/taglistmodel.cpp
new file mode 100644
index 0000000..dd70b3f
--- /dev/null
+++ b/model/recipe/tags/taglistmodel.cpp
@@ -0,0 +1,44 @@
+#include "taglistmodel.h"
+
+TagListModel::TagListModel(){
+
+}
+
+int TagListModel::rowCount(const QModelIndex &parent) const{
+ return this->tags.size();
+}
+
+QVariant TagListModel::data(const QModelIndex &index, int role) const{
+ string displayString = this->tags[index.row()].getValue();
+ switch(role){
+ case Qt::DisplayRole:
+ return QString::fromStdString(displayString);
+ }
+
+ return QVariant();
+}
+
+void TagListModel::setTags(vector tags){
+ this->tags = tags;
+ QModelIndex index = createIndex(0, 0);
+ QModelIndex bottomIndex = createIndex(this->tags.size()-1, 0);
+ emit dataChanged(index, bottomIndex);
+}
+
+bool TagListModel::addTag(RecipeTag tag){
+ //Add only if it's different.
+ for (unsigned int i = 0; i < this->tags.size(); i++){
+ if (!this->tags[i].getValue().compare(tag.getValue())){
+ return false;
+ }
+ }
+ this->tags.push_back(tag);
+ QModelIndex index = createIndex(this->tags.size()-1, 0);
+ QModelIndex bottomIndex = createIndex(this->tags.size()-1, 0);
+ emit dataChanged(index, bottomIndex);
+ return true;
+}
+
+vector TagListModel::getTags(){
+ return this->tags;
+}
diff --git a/model/recipe/tags/taglistmodel.h b/model/recipe/tags/taglistmodel.h
new file mode 100644
index 0000000..e524e23
--- /dev/null
+++ b/model/recipe/tags/taglistmodel.h
@@ -0,0 +1,24 @@
+#ifndef TAGLISTMODEL_H
+#define TAGLISTMODEL_H
+
+#include
+
+#include "model/recipe/tags/recipetag.h"
+
+class TagListModel : public QAbstractListModel
+{
+ public:
+ TagListModel();
+ //Overridden methods.
+ int rowCount(const QModelIndex &parent = QModelIndex()) const override;
+ QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
+
+
+ void setTags(vector tags);
+ bool addTag(RecipeTag tag);
+ vector getTags();
+ private:
+ vector tags;
+};
+
+#endif // TAGLISTMODEL_H