/* -*- c++ -*- */ /*************************************************************************** * database_krecipes.cc * * Sun Apr 24 18:28:00 CEST 2022 * Copyright 2022 Bent Bisballe Nyeng * deva@aasimon.org ****************************************************************************/ /* * This file is part of Qookie. * * Qookie is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * Qookie is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Qookie; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ #include "database_krecipes.h" #include #include // https://www.tutorialspoint.com/sqlite/sqlite_c_cpp.htm DatabaseKrecipes::DatabaseKrecipes(const std::string& file) { auto rc = sqlite3_open(file.data(), &db); // https://sqlite.org/c3ref/open.html if(rc) { std::cerr << "Can't open database: " << sqlite3_errmsg(db) << '\n'; return; } else { std::cout << "Opened database successfully\n"; } } DatabaseKrecipes::~DatabaseKrecipes() { sqlite3_close(db); } namespace { std::string getString(sqlite3_stmt *statement, int index) { int size = sqlite3_column_bytes(statement, index); std::string str; str.append((const char*)sqlite3_column_blob(statement, index), size); return str; } } // :: std::deque DatabaseKrecipes::getRecipes() { std::deque items; std::string sql = "select id, title, photo from recipes"; sqlite3_stmt *statement; if(sqlite3_prepare_v2(db, sql.data(), sql.length(), &statement, 0) != SQLITE_OK) { std::cerr << "sqlite3_prepare_v2 failed: " << sqlite3_errmsg(db) << '\n'; return {}; } int result = 0; while(true) { RecipeItem item; result = sqlite3_step(statement); if(result == SQLITE_ROW) { item.db = DatabaseSource::KRecipes; item.id = sqlite3_column_int(statement, 0); item.title = getString(statement, 1); item.image = getString(statement, 2); } else { break; } items.push_back(item); } return items; } Recipe DatabaseKrecipes::getRecipe(int id) { bool found{false}; Recipe recipe; { std::string sql = "select id, title, photo, instructions, prep_time, yield_amount, yield_type_id from recipes where id=" + std::to_string(id); sqlite3_stmt *statement; if(sqlite3_prepare_v2(db, sql.data(), sql.length(), &statement, 0) != SQLITE_OK) { std::cerr << "sqlite3_prepare_v2 failed: " << sqlite3_errmsg(db) << '\n'; return {}; } int result = 0; while(true) { result = sqlite3_step(statement); if(result == SQLITE_ROW) { recipe.db = DatabaseSource::KRecipes; recipe.id = sqlite3_column_int(statement, 0); recipe.title = getString(statement, 1); recipe.description = {}; // not used by krecipes recipe.image = getString(statement, 2); recipe.instructions = getString(statement, 3); recipe.source = {}; // set later recipe.cuisine = {}; // not used by krecipes recipe.cooktime = {}; // not used by krecipes recipe.preptime = 0; std::string duration = getString(statement, 4); // "hh:mm:ss" format try { int h = std::stoi(duration.substr(0, 2)); int m = std::stoi(duration.substr(3, 2)); int s = std::stoi(duration.substr(6, 2)); recipe.preptime = s + m * 60 + h * 60 * 60; } catch(...) { } recipe.yields = sqlite3_column_double(statement, 5); recipe.yield_unit = getString(statement, 6); // indirection set later recipe.tags.push_back("N/A"); // not used by krecipes } else { break; } found = true; } } if(!found) { std::cerr << "id " << id << " not found\n"; return {}; } // // Get auhors list // { std::string sql = "select author_id from author_list where recipe_id=" + std::to_string(id); sqlite3_stmt *statement; if(sqlite3_prepare_v2(db, sql.data(), sql.length(), &statement, 0) != SQLITE_OK) { std::cerr << "sqlite3_prepare_v2 failed: " << sqlite3_errmsg(db) << '\n'; return {}; } int result = 0; while(true) { result = sqlite3_step(statement); if(result == SQLITE_ROW) { int author_id = sqlite3_column_int(statement, 0); { std::string sql = "select name from authors where id=" + std::to_string(author_id); sqlite3_stmt *statement; if(sqlite3_prepare_v2(db, sql.data(), sql.length(), &statement, 0) != SQLITE_OK) { std::cerr << "sqlite3_prepare_v2 failed: " << sqlite3_errmsg(db) << '\n'; return {}; } int result = 0; while(true) { result = sqlite3_step(statement); if(result == SQLITE_ROW) { if(!recipe.source.empty()) { recipe.source += ", "; } recipe.source += getString(statement, 0); } else { break; } } } } else { break; } } } // // Get yield unit // { std::string sql = "select name from yield_types where id=" + recipe.yield_unit; sqlite3_stmt *statement; if(sqlite3_prepare_v2(db, sql.data(), sql.length(), &statement, 0) != SQLITE_OK) { std::cerr << "sqlite3_prepare_v2 failed: " << sqlite3_errmsg(db) << '\n'; return {}; } int result = 0; while(true) { result = sqlite3_step(statement); if(result == SQLITE_ROW) { recipe.yield_unit = getString(statement, 0); } else { break; } } } if(recipe.yield_unit == "-1") { recipe.yield_unit = ""; } // // Get ingredients // { std::string sql = "select ingredient_id, amount, unit_id, group_id from ingredient_list where recipe_id=" + std::to_string(id) + " order by order_index asc"; sqlite3_stmt *statement; if(sqlite3_prepare_v2(db, sql.data(), sql.length(), &statement, 0) != SQLITE_OK) { std::cerr << "sqlite3_prepare_v2 failed: " << sqlite3_errmsg(db) << '\n'; return {}; } int last_group_id{-1}; // -1 is actually a valid id in the database int result = 0; recipe.ingredient_groups.push_back({}); while(true) { Ingredient ingredient; result = sqlite3_step(statement); if(result == SQLITE_ROW) { ingredient.item = getString(statement, 0); ingredient.amount = sqlite3_column_double(statement, 1); ingredient.unit = getString(statement, 2); int group_id = sqlite3_column_int(statement, 3); if(last_group_id != group_id) { // Look up group id name and append recipe.ingredient_groups.push_back({/*"Group-" + std::to_string(group_id), {}*/}); last_group_id = group_id; // // Get ingredient group name // { std::string sql = "select name from ingredient_groups where id=" + std::to_string(group_id); sqlite3_stmt *statement; if(sqlite3_prepare_v2(db, sql.data(), sql.length(), &statement, 0) != SQLITE_OK) { std::cerr << "sqlite3_prepare_v2 failed: " << sqlite3_errmsg(db) << '\n'; return {}; } int result = 0; while(true) { result = sqlite3_step(statement); if(result == SQLITE_ROW) { recipe.ingredient_groups.back().title = getString(statement, 0); } else { break; } } } } // // Get ingredient name // { std::string sql = "select name from ingredients where id=" + ingredient.item; sqlite3_stmt *statement; if(sqlite3_prepare_v2(db, sql.data(), sql.length(), &statement, 0) != SQLITE_OK) { std::cerr << "sqlite3_prepare_v2 failed: " << sqlite3_errmsg(db) << '\n'; return {}; } int result = 0; while(true) { result = sqlite3_step(statement); if(result == SQLITE_ROW) { ingredient.item = getString(statement, 0); } else { break; } } } // // Get ingredient amount unit // { std::string sql = "select name from units where id=" + ingredient.unit; sqlite3_stmt *statement; if(sqlite3_prepare_v2(db, sql.data(), sql.length(), &statement, 0) != SQLITE_OK) { std::cerr << "sqlite3_prepare_v2 failed: " << sqlite3_errmsg(db) << '\n'; return {}; } int result = 0; while(true) { result = sqlite3_step(statement); if(result == SQLITE_ROW) { ingredient.unit = getString(statement, 0); } else { break; } } } recipe.ingredient_groups.back().ingredients.push_back(ingredient); } else { break; } } } return recipe; }