mirror of
https://github.com/aaronpo97/the-biergarten-app.git
synced 2026-06-01 01:54:00 +00:00
Move pipeline directory
This commit is contained in:
24
tooling/pipeline/src/services/sqlite/build_database_path.cc
Normal file
24
tooling/pipeline/src/services/sqlite/build_database_path.cc
Normal file
@@ -0,0 +1,24 @@
|
||||
/**
|
||||
* @file services/sqlite/build_database_path.cc
|
||||
* @brief SqliteExportService::BuildDatabasePath() implementation.
|
||||
*/
|
||||
|
||||
#include <filesystem>
|
||||
#include <string>
|
||||
|
||||
#include "services/sqlite_export_service.h"
|
||||
|
||||
std::filesystem::path SqliteExportService::BuildDatabasePath() const {
|
||||
std::filesystem::path base_filename("biergarten_seed_" + run_timestamp_utc_ +
|
||||
".sqlite");
|
||||
std::filesystem::path candidate =
|
||||
std::filesystem::current_path() / base_filename;
|
||||
|
||||
for (int suffix = 1; std::filesystem::exists(candidate); ++suffix) {
|
||||
candidate = std::filesystem::current_path() /
|
||||
std::filesystem::path("biergarten_seed_" + run_timestamp_utc_ +
|
||||
"-" + std::to_string(suffix) + ".sqlite");
|
||||
}
|
||||
|
||||
return candidate;
|
||||
}
|
||||
28
tooling/pipeline/src/services/sqlite/build_location_key.cc
Normal file
28
tooling/pipeline/src/services/sqlite/build_location_key.cc
Normal file
@@ -0,0 +1,28 @@
|
||||
/**
|
||||
* @file services/sqlite/build_location_key.cc
|
||||
* @brief SqliteExportService::BuildLocationKey() implementation.
|
||||
*/
|
||||
|
||||
#include <iomanip>
|
||||
#include <sstream>
|
||||
|
||||
#include "services/sqlite_export_service.h"
|
||||
#include "services/sqlite_export_service_helpers.h"
|
||||
|
||||
constexpr int kLocationPrecision = 17;
|
||||
|
||||
std::string SqliteExportService::BuildLocationKey(const Location& location) {
|
||||
std::ostringstream key_stream;
|
||||
key_stream << location.city << '\n'
|
||||
<< location.state_province << '\n'
|
||||
<< location.iso3166_2 << '\n'
|
||||
<< location.country << '\n'
|
||||
<< location.iso3166_1 << '\n'
|
||||
<< std::setprecision(kLocationPrecision) << location.latitude
|
||||
<< '\n'
|
||||
<< std::setprecision(kLocationPrecision) << location.longitude
|
||||
<< '\n'
|
||||
<< sqlite_export_service_internal::SerializeLocalLanguages(
|
||||
location.local_languages);
|
||||
return key_stream.str();
|
||||
}
|
||||
30
tooling/pipeline/src/services/sqlite/finalize.cc
Normal file
30
tooling/pipeline/src/services/sqlite/finalize.cc
Normal file
@@ -0,0 +1,30 @@
|
||||
/**
|
||||
* @file services/sqlite/finalize.cc
|
||||
* @brief SqliteExportService::Finalize() implementation.
|
||||
*/
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
#include "services/sqlite_export_service.h"
|
||||
#include "services/sqlite_export_service_helpers.h"
|
||||
|
||||
void SqliteExportService::Finalize() {
|
||||
if (db_handle_ == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
FinalizeStatements();
|
||||
if (transaction_open_) {
|
||||
sqlite_export_service_internal::ExecSql(
|
||||
db_handle_, "COMMIT;", "Failed to commit SQLite transaction");
|
||||
transaction_open_ = false;
|
||||
}
|
||||
|
||||
db_handle_.reset();
|
||||
location_cache_.clear();
|
||||
} catch (...) {
|
||||
RollbackAndCloseNoThrow();
|
||||
throw;
|
||||
}
|
||||
}
|
||||
11
tooling/pipeline/src/services/sqlite/finalize_statements.cc
Normal file
11
tooling/pipeline/src/services/sqlite/finalize_statements.cc
Normal file
@@ -0,0 +1,11 @@
|
||||
/**
|
||||
* @file services/sqlite/finalize_statements.cc
|
||||
* @brief SqliteExportService::FinalizeStatements() implementation.
|
||||
*/
|
||||
|
||||
#include "services/sqlite_export_service.h"
|
||||
|
||||
void SqliteExportService::FinalizeStatements() noexcept {
|
||||
insert_brewery_stmt_.reset();
|
||||
insert_location_stmt_.reset();
|
||||
}
|
||||
39
tooling/pipeline/src/services/sqlite/initialize.cc
Normal file
39
tooling/pipeline/src/services/sqlite/initialize.cc
Normal file
@@ -0,0 +1,39 @@
|
||||
/**
|
||||
* @file services/sqlite/initialize.cc
|
||||
* @brief SqliteExportService::Initialize() implementation.
|
||||
*/
|
||||
|
||||
#include <filesystem>
|
||||
#include <memory>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
|
||||
#include "services/sqlite_export_service.h"
|
||||
#include "services/sqlite_export_service_helpers.h"
|
||||
|
||||
void SqliteExportService::Initialize() {
|
||||
if (db_handle_ != nullptr) {
|
||||
throw std::runtime_error("SQLite export service is already initialized");
|
||||
}
|
||||
|
||||
run_timestamp_utc_ = date_time_provider_->GetUtcTimestamp();
|
||||
database_path_ = BuildDatabasePath();
|
||||
std::filesystem::create_directories(database_path_.parent_path());
|
||||
|
||||
db_handle_ = sqlite_export_service_internal::OpenDatabase(database_path_);
|
||||
|
||||
try {
|
||||
sqlite_export_service_internal::ExecSql(
|
||||
db_handle_, "PRAGMA foreign_keys = ON;",
|
||||
"Failed to enable SQLite foreign keys");
|
||||
InitializeSchema();
|
||||
PrepareStatements();
|
||||
sqlite_export_service_internal::ExecSql(
|
||||
db_handle_, "BEGIN IMMEDIATE TRANSACTION;",
|
||||
"Failed to begin SQLite transaction");
|
||||
transaction_open_ = true;
|
||||
} catch (...) {
|
||||
RollbackAndCloseNoThrow();
|
||||
throw;
|
||||
}
|
||||
}
|
||||
16
tooling/pipeline/src/services/sqlite/initialize_schema.cc
Normal file
16
tooling/pipeline/src/services/sqlite/initialize_schema.cc
Normal file
@@ -0,0 +1,16 @@
|
||||
/**
|
||||
* @file services/sqlite/initialize_schema.cc
|
||||
* @brief SqliteExportService::InitializeSchema() implementation.
|
||||
*/
|
||||
|
||||
#include "services/sqlite_export_service.h"
|
||||
#include "services/sqlite_export_service_helpers.h"
|
||||
|
||||
void SqliteExportService::InitializeSchema() {
|
||||
sqlite_export_service_internal::ExecSql(
|
||||
db_handle_, sqlite_export_service_internal::kCreateLocationsTableSql,
|
||||
"Failed to create SQLite locations table");
|
||||
sqlite_export_service_internal::ExecSql(
|
||||
db_handle_, sqlite_export_service_internal::kCreateBreweriesTableSql,
|
||||
"Failed to create SQLite breweries table");
|
||||
}
|
||||
16
tooling/pipeline/src/services/sqlite/prepare_statements.cc
Normal file
16
tooling/pipeline/src/services/sqlite/prepare_statements.cc
Normal file
@@ -0,0 +1,16 @@
|
||||
/**
|
||||
* @file services/sqlite/prepare_statements.cc
|
||||
* @brief SqliteExportService::PrepareStatements() implementation.
|
||||
*/
|
||||
|
||||
#include "services/sqlite_export_service.h"
|
||||
#include "services/sqlite_export_service_helpers.h"
|
||||
|
||||
void SqliteExportService::PrepareStatements() {
|
||||
insert_location_stmt_ = sqlite_export_service_internal::PrepareStatement(
|
||||
db_handle_, sqlite_export_service_internal::kInsertLocationSql,
|
||||
"Failed to prepare SQLite location insert statement");
|
||||
insert_brewery_stmt_ = sqlite_export_service_internal::PrepareStatement(
|
||||
db_handle_, sqlite_export_service_internal::kInsertBrewerySql,
|
||||
"Failed to prepare SQLite brewery insert statement");
|
||||
}
|
||||
100
tooling/pipeline/src/services/sqlite/process_record.cc
Normal file
100
tooling/pipeline/src/services/sqlite/process_record.cc
Normal file
@@ -0,0 +1,100 @@
|
||||
/**
|
||||
* @file services/sqlite/process_record.cc
|
||||
* @brief SqliteExportService::ProcessRecord() implementation.
|
||||
*/
|
||||
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
|
||||
#include "services/sqlite_export_service.h"
|
||||
#include "services/sqlite_export_service_helpers.h"
|
||||
|
||||
void SqliteExportService::ProcessRecord(const GeneratedBrewery& brewery) {
|
||||
if (db_handle_ == nullptr || !transaction_open_) {
|
||||
throw std::runtime_error("SQLite export service is not initialized");
|
||||
}
|
||||
|
||||
const std::string location_key = BuildLocationKey(brewery.location);
|
||||
const auto cached_location = location_cache_.find(location_key);
|
||||
sqlite3_int64 location_id = 0;
|
||||
|
||||
if (cached_location != location_cache_.end()) {
|
||||
location_id = cached_location->second;
|
||||
} else {
|
||||
const std::string local_languages_json =
|
||||
sqlite_export_service_internal::SerializeLocalLanguages(
|
||||
brewery.location.local_languages);
|
||||
|
||||
sqlite_export_service_internal::BindText(
|
||||
insert_location_stmt_,
|
||||
sqlite_export_service_internal::kLocationCityBindIndex,
|
||||
brewery.location.city, "Failed to bind SQLite location city");
|
||||
sqlite_export_service_internal::BindText(
|
||||
insert_location_stmt_,
|
||||
sqlite_export_service_internal::kLocationStateProvinceBindIndex,
|
||||
brewery.location.state_province,
|
||||
"Failed to bind SQLite location state/province");
|
||||
sqlite_export_service_internal::BindText(
|
||||
insert_location_stmt_,
|
||||
sqlite_export_service_internal::kLocationIso31662BindIndex,
|
||||
brewery.location.iso3166_2,
|
||||
"Failed to bind SQLite location ISO 3166-2 code");
|
||||
sqlite_export_service_internal::BindText(
|
||||
insert_location_stmt_,
|
||||
sqlite_export_service_internal::kLocationCountryBindIndex,
|
||||
brewery.location.country, "Failed to bind SQLite location country");
|
||||
sqlite_export_service_internal::BindText(
|
||||
insert_location_stmt_,
|
||||
sqlite_export_service_internal::kLocationIso31661BindIndex,
|
||||
brewery.location.iso3166_1,
|
||||
"Failed to bind SQLite location ISO 3166-1 code");
|
||||
sqlite_export_service_internal::BindText(
|
||||
insert_location_stmt_,
|
||||
sqlite_export_service_internal::kLocationLanguagesBindIndex,
|
||||
local_languages_json, "Failed to bind SQLite location languages");
|
||||
sqlite_export_service_internal::BindDouble(
|
||||
insert_location_stmt_,
|
||||
sqlite_export_service_internal::kLocationLatitudeBindIndex,
|
||||
brewery.location.latitude, "Failed to bind SQLite location latitude");
|
||||
sqlite_export_service_internal::BindDouble(
|
||||
insert_location_stmt_,
|
||||
sqlite_export_service_internal::kLocationLongitudeBindIndex,
|
||||
brewery.location.longitude, "Failed to bind SQLite location longitude");
|
||||
|
||||
sqlite_export_service_internal::StepStatement(
|
||||
db_handle_, insert_location_stmt_,
|
||||
"Failed to insert SQLite location row");
|
||||
|
||||
location_id = sqlite_export_service_internal::LastInsertRowId(db_handle_);
|
||||
location_cache_.emplace(location_key, location_id);
|
||||
sqlite_export_service_internal::ResetStatement(insert_location_stmt_);
|
||||
}
|
||||
|
||||
sqlite_export_service_internal::BindInt64(
|
||||
insert_brewery_stmt_,
|
||||
sqlite_export_service_internal::kBreweryLocationIdBindIndex, location_id,
|
||||
"Failed to bind SQLite brewery location id");
|
||||
sqlite_export_service_internal::BindText(
|
||||
insert_brewery_stmt_,
|
||||
sqlite_export_service_internal::kBreweryEnglishNameBindIndex,
|
||||
brewery.brewery.name_en, "Failed to bind SQLite brewery English name");
|
||||
sqlite_export_service_internal::BindText(
|
||||
insert_brewery_stmt_,
|
||||
sqlite_export_service_internal::kBreweryEnglishDescriptionBindIndex,
|
||||
brewery.brewery.description_en,
|
||||
"Failed to bind SQLite brewery English description");
|
||||
sqlite_export_service_internal::BindText(
|
||||
insert_brewery_stmt_,
|
||||
sqlite_export_service_internal::kBreweryLocalNameBindIndex,
|
||||
brewery.brewery.name_local, "Failed to bind SQLite brewery local name");
|
||||
sqlite_export_service_internal::BindText(
|
||||
insert_brewery_stmt_,
|
||||
sqlite_export_service_internal::kBreweryLocalDescriptionBindIndex,
|
||||
brewery.brewery.description_local,
|
||||
"Failed to bind SQLite brewery local description");
|
||||
|
||||
sqlite_export_service_internal::StepStatement(
|
||||
db_handle_, insert_brewery_stmt_, "Failed to insert SQLite brewery row");
|
||||
|
||||
sqlite_export_service_internal::ResetStatement(insert_brewery_stmt_);
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
/**
|
||||
* @file services/sqlite/rollback_and_close_no_throw.cc
|
||||
* @brief SqliteExportService::RollbackAndCloseNoThrow() implementation.
|
||||
*/
|
||||
|
||||
#include "services/sqlite_export_service.h"
|
||||
|
||||
void SqliteExportService::RollbackAndCloseNoThrow() noexcept {
|
||||
if (db_handle_ == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (transaction_open_) {
|
||||
sqlite_export_service_internal::RollbackTransactionNoThrow(db_handle_);
|
||||
transaction_open_ = false;
|
||||
}
|
||||
|
||||
FinalizeStatements();
|
||||
db_handle_.reset();
|
||||
location_cache_.clear();
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
/**
|
||||
* @file services/sqlite/sqlite_export_service.cc
|
||||
* @brief SqliteExportService constructor and destructor implementation.
|
||||
*/
|
||||
|
||||
#include "services/sqlite_export_service.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
SqliteExportService::SqliteExportService()
|
||||
: date_time_provider_(std::make_unique<SystemDateTimeProvider>()) {}
|
||||
|
||||
SqliteExportService::~SqliteExportService() {
|
||||
if (db_handle_ != nullptr) {
|
||||
RollbackAndCloseNoThrow();
|
||||
}
|
||||
}
|
||||
61
tooling/pipeline/src/services/wikipedia/fetch_extract.cc
Normal file
61
tooling/pipeline/src/services/wikipedia/fetch_extract.cc
Normal file
@@ -0,0 +1,61 @@
|
||||
/**
|
||||
* @file wikipedia/fetch_extract.cc
|
||||
* @brief WikipediaService::FetchExtract() implementation.
|
||||
*/
|
||||
|
||||
#include <spdlog/spdlog.h>
|
||||
|
||||
#include <boost/json.hpp>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
#include "services/wikipedia_service.h"
|
||||
|
||||
std::string WikipediaService::FetchExtract(std::string_view query) {
|
||||
const std::string cache_key(query);
|
||||
const auto cache_it = this->extract_cache_.find(cache_key);
|
||||
if (cache_it != this->extract_cache_.end()) {
|
||||
return cache_it->second;
|
||||
}
|
||||
|
||||
const std::string encoded = this->client_->UrlEncode(cache_key);
|
||||
const std::string url =
|
||||
"https://en.wikipedia.org/w/api.php?action=query&titles=" + encoded +
|
||||
"&prop=extracts&explaintext=1&format=json";
|
||||
|
||||
const std::string body = this->client_->Get(url);
|
||||
|
||||
boost::system::error_code parse_error;
|
||||
boost::json::value doc = boost::json::parse(body, parse_error);
|
||||
|
||||
if (!parse_error && doc.is_object()) {
|
||||
try {
|
||||
auto& pages = doc.at("query").at("pages").get_object();
|
||||
if (!pages.empty()) {
|
||||
auto& page = pages.begin()->value().get_object();
|
||||
if (page.contains("extract") && page.at("extract").is_string()) {
|
||||
const std::string_view extract_view = page.at("extract").as_string();
|
||||
std::string extract(extract_view);
|
||||
|
||||
spdlog::debug("WikipediaService fetched {} chars for '{}'",
|
||||
extract.size(), query);
|
||||
|
||||
this->extract_cache_.emplace(cache_key, extract);
|
||||
return extract;
|
||||
}
|
||||
}
|
||||
this->extract_cache_.emplace(cache_key, std::string{});
|
||||
} catch (const std::exception& e) {
|
||||
spdlog::warn(
|
||||
"WikipediaService: failed to parse response structure for '{}': "
|
||||
"{}",
|
||||
query, e.what());
|
||||
return {};
|
||||
}
|
||||
} else if (parse_error) {
|
||||
spdlog::warn("WikipediaService: JSON parse error for '{}': {}", query,
|
||||
parse_error.message());
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
47
tooling/pipeline/src/services/wikipedia/get_summary.cc
Normal file
47
tooling/pipeline/src/services/wikipedia/get_summary.cc
Normal file
@@ -0,0 +1,47 @@
|
||||
/**
|
||||
* @file wikipedia/get_summary.cc
|
||||
* @brief WikipediaService::GetLocationContext() implementation.
|
||||
*/
|
||||
|
||||
#include <spdlog/spdlog.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "services/wikipedia_service.h"
|
||||
|
||||
std::string WikipediaService::GetLocationContext(const Location& loc) {
|
||||
if (!client_) {
|
||||
return {};
|
||||
}
|
||||
|
||||
std::string result;
|
||||
|
||||
std::string region_query(loc.city);
|
||||
if (!loc.country.empty()) {
|
||||
region_query += ", ";
|
||||
region_query += loc.country;
|
||||
}
|
||||
|
||||
const std::string beer_query = "beer in " + loc.country;
|
||||
const std::string city_beer_query = "beer in " + loc.city;
|
||||
|
||||
auto append_extract = [&result](const std::string& extract) -> void {
|
||||
if (extract.empty()) {
|
||||
return;
|
||||
}
|
||||
if (!result.empty()) {
|
||||
result += "\n\n";
|
||||
}
|
||||
result += extract;
|
||||
};
|
||||
|
||||
try {
|
||||
append_extract(FetchExtract(region_query));
|
||||
append_extract(FetchExtract(beer_query));
|
||||
append_extract(FetchExtract(city_beer_query));
|
||||
} catch (const std::runtime_error& e) {
|
||||
spdlog::debug("WikipediaService lookup failed for '{}': {}", region_query,
|
||||
e.what());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
11
tooling/pipeline/src/services/wikipedia/wikipedia_service.cc
Normal file
11
tooling/pipeline/src/services/wikipedia/wikipedia_service.cc
Normal file
@@ -0,0 +1,11 @@
|
||||
/**
|
||||
* @file services/wikipedia/wikipedia_service.cc
|
||||
* @brief WikipediaService constructor implementation.
|
||||
*/
|
||||
|
||||
#include "services/wikipedia_service.h"
|
||||
|
||||
#include <utility>
|
||||
|
||||
WikipediaService::WikipediaService(std::unique_ptr<WebClient> client)
|
||||
: client_(std::move(client)) {}
|
||||
Reference in New Issue
Block a user