From 305e5c3698e7b1a891ecfa6632f3d754fe0fd6bf Mon Sep 17 00:00:00 2001 From: Dan Paulat Date: Tue, 25 Oct 2022 23:21:26 -0500 Subject: [PATCH] Add county database interface and test --- CMakeLists.txt | 1 + scwx-qt/scwx-qt.cmake | 8 +- .../source/scwx/qt/config/county_database.cpp | 145 ++++++++++++++++++ .../source/scwx/qt/config/county_database.hpp | 22 +++ .../scwx/qt/config/county_database.test.cpp | 38 +++++ test/test.cmake | 3 +- 6 files changed, 214 insertions(+), 3 deletions(-) create mode 100644 scwx-qt/source/scwx/qt/config/county_database.cpp create mode 100644 scwx-qt/source/scwx/qt/config/county_database.hpp create mode 100644 test/source/scwx/qt/config/county_database.test.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 33eb5bd6..6e92d732 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -21,6 +21,7 @@ conan_cmake_configure(REQUIRES aws-sdk-cpp/1.9.234 gtest/cci.20210126 openssl/1.1.1q spdlog/1.10.0 + sqlite3/3.39.4 vulkan-loader/1.3.221 GENERATORS cmake cmake_find_package diff --git a/scwx-qt/scwx-qt.cmake b/scwx-qt/scwx-qt.cmake index 849cb395..74ce7840 100644 --- a/scwx-qt/scwx-qt.cmake +++ b/scwx-qt/scwx-qt.cmake @@ -16,6 +16,7 @@ find_package(Freetype) find_package(geographiclib) find_package(glm) find_package(Python COMPONENTS Interpreter) +find_package(SQLite3) find_package(QT NAMES Qt6 COMPONENTS Gui @@ -39,8 +40,10 @@ set(SRC_EXE_MAIN source/scwx/qt/main/main.cpp) set(HDR_MAIN source/scwx/qt/main/main_window.hpp) set(SRC_MAIN source/scwx/qt/main/main_window.cpp) set(UI_MAIN source/scwx/qt/main/main_window.ui) -set(HDR_CONFIG source/scwx/qt/config/radar_site.hpp) -set(SRC_CONFIG source/scwx/qt/config/radar_site.cpp) +set(HDR_CONFIG source/scwx/qt/config/county_database.hpp + source/scwx/qt/config/radar_site.hpp) +set(SRC_CONFIG source/scwx/qt/config/county_database.cpp + source/scwx/qt/config/radar_site.cpp) set(SRC_EXTERNAL source/scwx/qt/external/stb_rect_pack.cpp) set(HDR_GL source/scwx/qt/gl/gl.hpp source/scwx/qt/gl/gl_context.hpp @@ -309,6 +312,7 @@ target_link_libraries(scwx-qt PUBLIC Qt${QT_VERSION_MAJOR}::Widgets freetype-gl GeographicLib::GeographicLib glm::glm + SQLite::SQLite3 wxdata) target_link_libraries(supercell-wx PRIVATE scwx-qt diff --git a/scwx-qt/source/scwx/qt/config/county_database.cpp b/scwx-qt/source/scwx/qt/config/county_database.cpp new file mode 100644 index 00000000..28c4aa6d --- /dev/null +++ b/scwx-qt/source/scwx/qt/config/county_database.cpp @@ -0,0 +1,145 @@ +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +namespace scwx +{ +namespace qt +{ +namespace config +{ +namespace CountyDatabase +{ + +static const std::string logPrefix_ = "scwx::qt::config::county_database"; +static const auto logger_ = scwx::util::Logger::Create(logPrefix_); + +static const std::string countyDatabaseFilename_ = ":/res/db/counties.db"; + +static std::unordered_map countyMap_; +static std::shared_mutex countyMutex_; + +void CountyDatabase::Initialize() +{ + logger_->debug("Loading database"); + + // Generate UUID for temporary file + boost::uuids::uuid uuid = boost::uuids::random_generator()(); + + std::string tempPath { + QStandardPaths::writableLocation(QStandardPaths::TempLocation) + .toStdString()}; + std::string countyDatabaseCache {tempPath + "/scwx-" + + boost::uuids::to_string(uuid)}; + + // Create cache directory if it doesn't exist + if (!std::filesystem::exists(tempPath)) + { + if (!std::filesystem::create_directories(tempPath)) + { + logger_->error("Unable to create temp directory: \"{}\"", tempPath); + return; + } + } + + // Remove existing county database if it exists + if (std::filesystem::exists(countyDatabaseCache)) + { + std::filesystem::remove(countyDatabaseCache); + } + + // Create a fresh copy of the county database in the temporary directory + QFile countyDatabaseFile(QString::fromStdString(countyDatabaseFilename_)); + if (!countyDatabaseFile.copy(QString::fromStdString(countyDatabaseCache))) + { + logger_->error("Unable to create cached copy of database: \"{}\" ({})", + countyDatabaseCache, + countyDatabaseFile.errorString().toStdString()); + return; + } + + // Open database + sqlite3* db; + int rc; + char* errorMessage = nullptr; + + rc = sqlite3_open(countyDatabaseCache.c_str(), &db); + if (rc != SQLITE_OK) + { + logger_->error("Unable to open database: \"{}\"", countyDatabaseCache); + sqlite3_close(db); + std::filesystem::remove(countyDatabaseCache); + return; + } + + // Database is open, acquire lock + std::unique_lock lock(countyMutex_); + + // Query database for counties + rc = sqlite3_exec( + db, + "SELECT * FROM counties", + [](void* /* param */, + int columns, + char** columnText, + char** /* columnName */) -> int + { + int status = 0; + + if (columns == 2) + { + countyMap_.emplace(columnText[0], columnText[1]); + } + else + { + logger_->error( + "Database format error, invalid number of columns: {}", columns); + status = -1; + } + + return status; + }, + nullptr, + &errorMessage); + if (rc != SQLITE_OK) + { + logger_->error("SQL error: {}", errorMessage); + sqlite3_free(errorMessage); + } + + // Finished populating county map, release lock + lock.unlock(); + + // Close database + sqlite3_close(db); + + // Remove temporary file + std::filesystem::remove(countyDatabaseCache); +} + +std::string GetCountyName(const std::string& id) +{ + std::shared_lock lock(countyMutex_); + + auto it = countyMap_.find(id); + if (it != countyMap_.cend()) + { + return it->second; + } + + return id; +} + +} // namespace CountyDatabase +} // namespace config +} // namespace qt +} // namespace scwx diff --git a/scwx-qt/source/scwx/qt/config/county_database.hpp b/scwx-qt/source/scwx/qt/config/county_database.hpp new file mode 100644 index 00000000..e75431ac --- /dev/null +++ b/scwx-qt/source/scwx/qt/config/county_database.hpp @@ -0,0 +1,22 @@ +#pragma once + +#include +#include +#include + +namespace scwx +{ +namespace qt +{ +namespace config +{ +namespace CountyDatabase +{ + +void Initialize(); +std::string GetCountyName(const std::string& id); + +} // namespace CountyDatabase +} // namespace config +} // namespace qt +} // namespace scwx diff --git a/test/source/scwx/qt/config/county_database.test.cpp b/test/source/scwx/qt/config/county_database.test.cpp new file mode 100644 index 00000000..d9f0b3e2 --- /dev/null +++ b/test/source/scwx/qt/config/county_database.test.cpp @@ -0,0 +1,38 @@ +#include + +#include + +namespace scwx +{ +namespace qt +{ +namespace config +{ + +class CountyDatabaseTest : + public testing::TestWithParam> +{ + virtual void SetUp() { scwx::qt::config::CountyDatabase::Initialize(); } +}; + +TEST_P(CountyDatabaseTest, CountyName) +{ + auto& [id, name] = GetParam(); + + std::string actualName = CountyDatabase::GetCountyName(id); + + EXPECT_EQ(actualName, name); +} + +INSTANTIATE_TEST_SUITE_P( + CountyDatabase, + CountyDatabaseTest, + testing::Values(std::make_pair("AZC013", "Maricopa"), + std::make_pair("MOC183", "St. Charles"), + std::make_pair("TXZ211", "Austin"), + std::make_pair("GMZ335", "Galveston Bay"), + std::make_pair("ANZ338", "New York Harbor"))); + +} // namespace config +} // namespace qt +} // namespace scwx diff --git a/test/test.cmake b/test/test.cmake index 63cfef6a..31b7bc50 100644 --- a/test/test.cmake +++ b/test/test.cmake @@ -17,7 +17,8 @@ set(SRC_COMMON_TESTS source/scwx/common/color_table.test.cpp source/scwx/common/products.test.cpp) set(SRC_PROVIDER_TESTS source/scwx/provider/aws_level2_data_provider.test.cpp source/scwx/provider/aws_level3_data_provider.test.cpp) -set(SRC_QT_CONFIG_TESTS source/scwx/qt/config/radar_site.test.cpp) +set(SRC_QT_CONFIG_TESTS source/scwx/qt/config/county_database.test.cpp + source/scwx/qt/config/radar_site.test.cpp) set(SRC_QT_MANAGER_TESTS source/scwx/qt/manager/settings_manager.test.cpp) set(SRC_UTIL_TESTS source/scwx/util/float.test.cpp source/scwx/util/rangebuf.test.cpp