From 397189c259b82f30f19d250ed812c487327d5e26 Mon Sep 17 00:00:00 2001 From: Lukas Forsberg Date: Mon, 2 Jun 2025 22:44:53 +0200 Subject: [PATCH] Possible to load basic data from the database --- .vscode/settings.json | 99 +++++++++++++++++++- CMakeLists.txt | 3 +- src/database/database.cpp | 78 +++++++++++++++- src/database/database.hpp | 14 ++- src/shadowrun/HtmxShAttributeList.cpp | 11 ++- src/shadowrun/HtmxShAttributeList.hpp | 2 + src/shadowrun/HtmxShCondition.cpp | 7 ++ src/shadowrun/HtmxShCondition.hpp | 3 + src/shadowrun/HtmxShItemList.cpp | 11 ++- src/shadowrun/HtmxShItemList.hpp | 2 + src/shadowrun/ShadowrunApi.cpp | 97 ++++++++++---------- src/shadowrun/ShadowrunCharacterForm.cpp | 25 ++++++ src/shadowrun/ShadowrunCharacterForm.hpp | 2 +- src/shadowrun/ShadowrunDb.cpp | 110 +++++++++++++++++++++++ src/shadowrun/ShadowrunDb.hpp | 20 +++++ 15 files changed, 426 insertions(+), 58 deletions(-) create mode 100644 src/shadowrun/ShadowrunDb.cpp create mode 100644 src/shadowrun/ShadowrunDb.hpp diff --git a/.vscode/settings.json b/.vscode/settings.json index 23a0c9f..bc67769 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,6 +1,103 @@ { "files.associations": { "text_encoding": "cpp", - "thread": "cpp" + "thread": "cpp", + "deque": "cpp", + "string": "cpp", + "vector": "cpp", + "any": "cpp", + "array": "cpp", + "atomic": "cpp", + "hash_map": "cpp", + "strstream": "cpp", + "barrier": "cpp", + "bit": "cpp", + "bitset": "cpp", + "cctype": "cpp", + "cfenv": "cpp", + "charconv": "cpp", + "chrono": "cpp", + "cinttypes": "cpp", + "clocale": "cpp", + "cmath": "cpp", + "codecvt": "cpp", + "compare": "cpp", + "complex": "cpp", + "concepts": "cpp", + "condition_variable": "cpp", + "coroutine": "cpp", + "csetjmp": "cpp", + "csignal": "cpp", + "cstdarg": "cpp", + "cstddef": "cpp", + "cstdint": "cpp", + "cstdio": "cpp", + "cstdlib": "cpp", + "cstring": "cpp", + "ctime": "cpp", + "cuchar": "cpp", + "cwchar": "cpp", + "cwctype": "cpp", + "forward_list": "cpp", + "list": "cpp", + "map": "cpp", + "set": "cpp", + "unordered_map": "cpp", + "unordered_set": "cpp", + "exception": "cpp", + "expected": "cpp", + "algorithm": "cpp", + "functional": "cpp", + "iterator": "cpp", + "memory": "cpp", + "memory_resource": "cpp", + "numeric": "cpp", + "optional": "cpp", + "random": "cpp", + "ratio": "cpp", + "regex": "cpp", + "source_location": "cpp", + "string_view": "cpp", + "system_error": "cpp", + "tuple": "cpp", + "type_traits": "cpp", + "utility": "cpp", + "flat_map": "cpp", + "flat_set": "cpp", + "format": "cpp", + "fstream": "cpp", + "future": "cpp", + "generator": "cpp", + "initializer_list": "cpp", + "iomanip": "cpp", + "iosfwd": "cpp", + "iostream": "cpp", + "istream": "cpp", + "latch": "cpp", + "limits": "cpp", + "mutex": "cpp", + "new": "cpp", + "numbers": "cpp", + "ostream": "cpp", + "print": "cpp", + "queue": "cpp", + "ranges": "cpp", + "scoped_allocator": "cpp", + "semaphore": "cpp", + "shared_mutex": "cpp", + "span": "cpp", + "spanstream": "cpp", + "sstream": "cpp", + "stack": "cpp", + "stacktrace": "cpp", + "stdexcept": "cpp", + "stdfloat": "cpp", + "stop_token": "cpp", + "streambuf": "cpp", + "syncstream": "cpp", + "typeindex": "cpp", + "typeinfo": "cpp", + "valarray": "cpp", + "variant": "cpp" } } \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index f807587..7972500 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -67,7 +67,8 @@ add_executable(${TARGET_NAME} src/shadowrun/ShadowrunCharacterForm.cpp src/shadowrun/ShadowrunApi.cpp src/shadowrun/ShadowrunApi.hpp - + src/shadowrun/ShadowrunDb.cpp + src/shadowrun/ShadowrunDb.hpp ) diff --git a/src/database/database.cpp b/src/database/database.cpp index f559b6c..31db99c 100644 --- a/src/database/database.cpp +++ b/src/database/database.cpp @@ -1,6 +1,8 @@ #include "crow.h" #include "database.hpp" +using namespace std; + Database::Database() : m_db(nullptr) {} @@ -9,6 +11,15 @@ Database::~Database() { sqlite3_close(m_db); } +sqlite3_stmt* Database::prepareStmt(const string& sql){ + sqlite3_stmt* stmt = nullptr; + if (sqlite3_prepare_v2(m_db, sql.c_str(), -1, &stmt, nullptr) != SQLITE_OK) { + CROW_LOG_ERROR << "Failed to prepare statement: " << sqlite3_errmsg(m_db); + return nullptr; + } + return stmt; +} + bool Database::exec(const char* sqlQuery) { char* errmsg = nullptr; int rc = sqlite3_exec(m_db, sqlQuery, nullptr, nullptr, &errmsg); @@ -20,13 +31,72 @@ bool Database::exec(const char* sqlQuery) { return true; } -bool Database::exec(const std::string& sqlQuery) -{ - exec(sqlQuery.c_str()); +bool Database::exec(const std::string& sqlQuery){ + return exec(sqlQuery.c_str()); +} + +map Database::getStrMap(const string& sql){ + sqlite3_stmt* stmt = prepareStmt(sql); + map map; + if (stmt == nullptr) + return map; + + while (sqlite3_step(stmt) == SQLITE_ROW) { + string key = reinterpret_cast(sqlite3_column_text(stmt, 0)); + string value = reinterpret_cast(sqlite3_column_text(stmt, 1)); + map[key] = value; + } + + sqlite3_finalize(stmt); + return map; +} + +set Database::getStrSet(const string& sql){ + sqlite3_stmt* stmt = prepareStmt(sql); + set vec; + if (stmt == nullptr) + return vec; + + while (sqlite3_step(stmt) == SQLITE_ROW) { + string s = reinterpret_cast(sqlite3_column_text(stmt, 0)); + vec.insert(s); + } + + sqlite3_finalize(stmt); + return vec; +} + +std::optional Database::getInt(const char* sql) { + sqlite3_stmt* stmt = prepareStmt(sql); + if (stmt == nullptr) + return {}; + + std::optional id; + if (sqlite3_step(stmt) == SQLITE_ROW) { + id = sqlite3_column_int64(stmt, 0); + } + + sqlite3_finalize(stmt); + return id; +} + +std::optional Database::insert(const char* sql) { + sqlite3_stmt* stmt = prepareStmt(sql); + if (stmt == nullptr) + return {}; + + if (sqlite3_step(stmt) != SQLITE_DONE) { + CROW_LOG_ERROR << "Insert failed: " << sqlite3_errmsg(m_db); + sqlite3_finalize(stmt); + return {}; + } + + sqlite3_finalize(stmt); + return sqlite3_last_insert_rowid(m_db); } bool Database::open(){ - int rc = sqlite3_open("example.db", &m_db); + int rc = sqlite3_open("app.db", &m_db); if (rc) { CROW_LOG_ERROR << "Can't open database: " << sqlite3_errmsg(m_db); return false; diff --git a/src/database/database.hpp b/src/database/database.hpp index 478ba44..e6dcdaf 100644 --- a/src/database/database.hpp +++ b/src/database/database.hpp @@ -3,7 +3,8 @@ #include "sqlite3.h" #include - +#include +#include class Database { public: @@ -14,7 +15,18 @@ public: bool exec(const char* sqlQuery); bool exec(const std::string& sqlQuery); + /// returns true if the sql statment returns at least one row + std::optional getInt(const char* sql); + + std::optional insert(const char* sql); + + std::set getStrSet(const std::string& sql); + + std::map Database::getStrMap(const std::string& sql); + private: + sqlite3_stmt* prepareStmt(const std::string& sql); + sqlite3* m_db; }; diff --git a/src/shadowrun/HtmxShAttributeList.cpp b/src/shadowrun/HtmxShAttributeList.cpp index 5a76fb9..d17bdff 100644 --- a/src/shadowrun/HtmxShAttributeList.cpp +++ b/src/shadowrun/HtmxShAttributeList.cpp @@ -8,7 +8,7 @@ HtmxShAttributeList::HtmxShAttributeList(const std::string& id, const vector{}", id); html += "
"; for (auto& item : itemList){ - string item_id = utils::to_id_format(id + "_" + item); + string item_id = utils::to_id_format(format("{}_{}", id, item)); html += format("", item, item_id); } @@ -19,8 +19,15 @@ HtmxShAttributeList::HtmxShAttributeList(const std::string& id, const std::vecto html += format("

{}

", id); html += "
"; for (auto& item : itemValueList){ - string item_id = utils::to_id_format(id + "_" + item.first); + string item_id = utils::to_id_format(format("{}_{}", id, item)); html += format("", item, item_id, item.second); } html += "
"; +} + +void HtmxShAttributeList::genIds(std::vector& vec, const std::string& id, const std::vector& itemList) +{ + for (auto& item : itemList){ + vec.push_back(utils::to_id_format(format("{}_{}", id, item))); + } } \ No newline at end of file diff --git a/src/shadowrun/HtmxShAttributeList.hpp b/src/shadowrun/HtmxShAttributeList.hpp index f220b06..64f9377 100644 --- a/src/shadowrun/HtmxShAttributeList.hpp +++ b/src/shadowrun/HtmxShAttributeList.hpp @@ -13,6 +13,8 @@ public: // create new item list where each item as a value HtmxShAttributeList(const std::string& id, const std::vector>& itemValueList); + + static void genIds(std::vector& vec, const std::string& id, const std::vector& itemList); }; #endif // HTMXSHATTRIBUTELIST_H \ No newline at end of file diff --git a/src/shadowrun/HtmxShCondition.cpp b/src/shadowrun/HtmxShCondition.cpp index 32f837e..8a65376 100644 --- a/src/shadowrun/HtmxShCondition.cpp +++ b/src/shadowrun/HtmxShCondition.cpp @@ -26,3 +26,10 @@ HtmxShCondition::HtmxShCondition(std::string id, size_t nbrOfBoxes) } html += "
"; } + +void HtmxShCondition::genIds(std::vector& vec, std::string id, size_t nbrOfBoxes) +{ + for (int i = 0; i < nbrOfBoxes; i++){ + vec.push_back(utils::to_id_format(format("{}_{}",id, i))); + } +} diff --git a/src/shadowrun/HtmxShCondition.hpp b/src/shadowrun/HtmxShCondition.hpp index 3ce877e..cba8415 100644 --- a/src/shadowrun/HtmxShCondition.hpp +++ b/src/shadowrun/HtmxShCondition.hpp @@ -2,12 +2,15 @@ #define __HTMXSHCONDITION_H__ #include +#include #include "HtmxObject.h" class HtmxShCondition : public HtmxObject { public: HtmxShCondition(std::string id, size_t nbrOfBoxes); + + static void genIds(std::vector& vec, std::string id, size_t nbrOfBoxes); }; #endif // __HTMXSHCONDITION_H__ \ No newline at end of file diff --git a/src/shadowrun/HtmxShItemList.cpp b/src/shadowrun/HtmxShItemList.cpp index 9a14d11..998c79f 100644 --- a/src/shadowrun/HtmxShItemList.cpp +++ b/src/shadowrun/HtmxShItemList.cpp @@ -40,4 +40,13 @@ HtmxShItemList::HtmxShItemList(const std::string& id, const std::vector& vec, const std::string& id, const std::vector& columns, size_t size) +{ + for (size_t i = 0; i < size; i++){ + for (auto& col : columns){ + vec.push_back(utils::to_id_format(format("{}_{}_{}", id, i, col))); + } + } +} diff --git a/src/shadowrun/HtmxShItemList.hpp b/src/shadowrun/HtmxShItemList.hpp index 47aa221..d231be8 100644 --- a/src/shadowrun/HtmxShItemList.hpp +++ b/src/shadowrun/HtmxShItemList.hpp @@ -13,6 +13,8 @@ public: // create new item list where each item as a value HtmxShItemList(const std::string& id, const std::vector>& itemValueList); + + static void genIds(std::vector& vec, const std::string& id, const std::vector& columns, size_t size); }; #endif // HTMXSHITEMLIST_H \ No newline at end of file diff --git a/src/shadowrun/ShadowrunApi.cpp b/src/shadowrun/ShadowrunApi.cpp index f7d399e..7d730b4 100644 --- a/src/shadowrun/ShadowrunApi.cpp +++ b/src/shadowrun/ShadowrunApi.cpp @@ -2,66 +2,71 @@ #include "ShadowrunApi.hpp" #include "ShadowrunCharacterForm.hpp" -#include "database.hpp" +#include "ShadowrunDb.hpp" +#include +#include +using namespace std; namespace shadowrun { -bool initDb() { - auto db = Database(); +static std::unordered_map parse_query_string(const std::string& query) { + std::unordered_map params; + std::istringstream stream(query); + std::string pair; - if (!db.open()){ - return false; + while (std::getline(stream, pair, '&')) { + auto pos = pair.find('='); + if (pos != std::string::npos) { + std::string key = pair.substr(0, pos); + std::string value = pair.substr(pos + 1); + params[key] = value; // You may want to URL-decode here + } } - // Create a tables - const char* create_sql_chars = "CREATE TABLE IF NOT EXISTS shadowrun_characters (" - "id INTEGER PRIMARY KEY," - "name TEXT," - "created_at DATETIME DEFAULT CURRENT_TIMESTAMP);"; - - if (!db.exec(create_sql_chars)){ - CROW_LOG_ERROR << "Failed to create shadowrun_characters table"; - return false; - } - - const char* create_sql_data = "CREATE TABLE IF NOT EXISTS shadowrun_data (" - "id INTEGER PRIMARY KEY," - "character_id INTEGER NOT NULL," - "name TEXT NOT NULL," - "value TEXT," - "created_at DATETIME DEFAULT CURRENT_TIMESTAMP," - "updated_at DATETIME DEFAULT CURRENT_TIMESTAMP," - "FOREIGN KEY (character_id) REFERENCES characters(id) ON DELETE CASCADE);"; - - if (!db.exec(create_sql_data)){ - CROW_LOG_ERROR << "Failed to create shadowrun_data table"; - return false; - } - return true; + return params; +} +static crow::response rsp(const std::string& msg){ + auto str = format("
" + "{}
", msg); + return crow::response{str}; } void initApi(crow::SimpleApp& app) { CROW_ROUTE(app, "/api/shadowrun/submit-character").methods("POST"_method)( [](const crow::request& req) { - auto params = crow::query_string(req.body); + auto params = parse_query_string(req.body); - std::string name = params.get("name") ? params.get("name") : ""; - std::string metatype = params.get("metatype") ? params.get("metatype") : ""; - std::string age = params.get("age") ? params.get("age") : ""; - // ... extract more fields as needed + auto name_data = params["Character-Info_Name"]; + if (name_data.empty()){ + CROW_LOG_WARNING << "Character without name submited, will not be saved"; + return rsp("Failed : Character without name submited, will not be saved"); + } - // Optionally save to a DB or do logic here + auto key = getKeyOfCharacter(name_data); + if (key < 0){ + CROW_LOG_ERROR << "Failed to create id of character : " << name_data; + return rsp("Failed to create id of character"); + } - // Return response HTML - std::ostringstream out; - out << "
" - << "Character " << name << " submitted successfully!" - << "
"; + vector> idValues; + idValues.reserve(ShadowrunCharacterForm::m_formIds.size()); + + for (auto& id : ShadowrunCharacterForm::m_formIds) { + auto data = params[id]; + if(! + data.empty()){ + idValues.push_back(make_pair(id, data)); + } + } - return crow::response{out.str()}; + if (!storeCharacterData(key, idValues)){ + CROW_LOG_ERROR << "Failed to store character data of " << name_data; + return rsp("Failed to store character data"); + }; + return rsp(format("Character {} submitted successfully", name_data)); }); CROW_ROUTE(app, "/api/shadowrun/character-form") @@ -69,9 +74,7 @@ void initApi(crow::SimpleApp& app) auto query = crow::query_string(req.url_params); std::string name = query.get("name") ? query.get("name") : ""; - // TODO: Load data from file or DB using `name` - std::string metatype = "Troll"; - int age = 28; + auto data = getCharacterData(getKeyOfCharacter(name)); return crow::response{ShadowrunCharacterForm().htmx()}; }); @@ -81,7 +84,7 @@ void initApi(crow::SimpleApp& app) std::ostringstream html; // Simulated character database - std::vector characters = { "Trogdor", "Alice", "Zigzag" }; + auto characters = getCharacters(); html << "
" << "