mirror of
https://github.com/aaronpo97/the-biergarten-app.git
synced 2026-07-16 17:47:22 +00:00
Pipeline: rename Location to City, wire postal code into breweries
This commit is contained in:
@@ -15,7 +15,7 @@
|
||||
|
||||
using ForenameList = std::vector<ForenameEntry>;
|
||||
using SurnameList = std::vector<std::string>;
|
||||
using LocationsList = std::vector<Location>;
|
||||
using CityList = std::vector<City>;
|
||||
using PersonasList = std::vector<UserPersona>;
|
||||
using ForenamesByCountryMap = std::unordered_map<std::string, ForenameList>;
|
||||
using SurnamesByCountryMap = std::unordered_map<std::string, SurnameList>;
|
||||
@@ -37,7 +37,7 @@ class ICuratedDataService {
|
||||
/**
|
||||
* @brief Loads all curated location records.
|
||||
*/
|
||||
virtual const LocationsList& LoadLocations() = 0;
|
||||
virtual const CityList& LoadCities() = 0;
|
||||
|
||||
/**
|
||||
* @brief Loads all curated persona records.
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
* CuratedJsonDataService.
|
||||
*/
|
||||
struct CuratedDataFilePaths {
|
||||
std::filesystem::path locations_path;
|
||||
std::filesystem::path cities_path;
|
||||
std::filesystem::path personas_path;
|
||||
std::filesystem::path forenames_path;
|
||||
std::filesystem::path surnames_path;
|
||||
@@ -27,7 +27,7 @@ struct CuratedDataFilePaths {
|
||||
*/
|
||||
class CuratedJsonDataService final : public ICuratedDataService {
|
||||
struct cache {
|
||||
LocationsList locations;
|
||||
CityList locations;
|
||||
PersonasList personas;
|
||||
ForenamesByCountryMap forenames_by_country;
|
||||
SurnamesByCountryMap surnames_by_country;
|
||||
@@ -43,9 +43,9 @@ class CuratedJsonDataService final : public ICuratedDataService {
|
||||
explicit CuratedJsonDataService(CuratedDataFilePaths filepaths);
|
||||
|
||||
/**
|
||||
* @brief Parses a JSON array file and returns all location records.
|
||||
* @brief Parses a JSON array file and returns all city records.
|
||||
*/
|
||||
const LocationsList& LoadLocations() override;
|
||||
const CityList& LoadCities() override;
|
||||
|
||||
/**
|
||||
* @brief Parses a JSON array file and returns all persona records.
|
||||
|
||||
@@ -20,7 +20,7 @@ class MockCuratedDataService final : public ICuratedDataService {
|
||||
public:
|
||||
MockCuratedDataService();
|
||||
|
||||
const LocationsList& LoadLocations() override;
|
||||
const CityList& LoadCities() override;
|
||||
|
||||
const PersonasList& LoadPersonas() override;
|
||||
|
||||
@@ -29,7 +29,7 @@ class MockCuratedDataService final : public ICuratedDataService {
|
||||
const SurnamesByCountryMap& LoadSurnamesByCountry() override;
|
||||
|
||||
private:
|
||||
LocationsList locations_;
|
||||
CityList locations_;
|
||||
PersonasList personas_;
|
||||
ForenamesByCountryMap forenames_by_country_;
|
||||
SurnamesByCountryMap surnames_by_country_;
|
||||
|
||||
@@ -45,7 +45,7 @@ class SqliteExportService final : public IExportService {
|
||||
void RollbackAndCloseNoThrow() noexcept;
|
||||
|
||||
[[nodiscard]] std::filesystem::path BuildDatabasePath() const;
|
||||
[[nodiscard]] static std::string BuildLocationKey(const Location& location);
|
||||
[[nodiscard]] static std::string BuildCityKey(const City& location);
|
||||
|
||||
/**
|
||||
* @brief Returns the row id for @p location, inserting it first if it has
|
||||
@@ -54,18 +54,18 @@ class SqliteExportService final : public IExportService {
|
||||
* Shared by both ProcessRecord() overloads so breweries and users
|
||||
* referencing the same location resolve to the same row.
|
||||
*/
|
||||
[[nodiscard]] sqlite3_int64 ResolveLocationId(const Location& location);
|
||||
[[nodiscard]] sqlite3_int64 ResolveCityId(const City& location);
|
||||
|
||||
std::unique_ptr<IDateTimeProvider> date_time_provider_;
|
||||
std::filesystem::path output_path_;
|
||||
std::string run_timestamp_utc_;
|
||||
std::filesystem::path database_path_;
|
||||
SqliteDatabaseHandle db_handle_;
|
||||
SqliteStatementHandle insert_location_stmt_;
|
||||
SqliteStatementHandle insert_city_stmt_;
|
||||
SqliteStatementHandle insert_brewery_stmt_;
|
||||
SqliteStatementHandle insert_user_stmt_;
|
||||
bool transaction_open_ = false;
|
||||
std::unordered_map<std::string, sqlite3_int64> location_cache_;
|
||||
std::unordered_map<std::string, sqlite3_int64> city_cache_;
|
||||
};
|
||||
|
||||
#endif // BIERGARTEN_PIPELINE_INCLUDES_SERVICES_DATABASE_SQLITE_EXPORT_SERVICE_H_
|
||||
|
||||
@@ -17,9 +17,9 @@
|
||||
|
||||
namespace sqlite_export_service_internal {
|
||||
|
||||
inline constexpr std::string_view kCreateLocationsTableSql = R"sql(
|
||||
inline constexpr std::string_view kCreateCitiesTableSql = R"sql(
|
||||
|
||||
CREATE TABLE IF NOT EXISTS locations (
|
||||
CREATE TABLE IF NOT EXISTS cities (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
city TEXT NOT NULL,
|
||||
state_province TEXT NOT NULL,
|
||||
@@ -38,15 +38,16 @@ inline constexpr std::string_view kCreateBreweriesTableSql = R"sql(
|
||||
|
||||
CREATE TABLE IF NOT EXISTS breweries (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
location_id INTEGER NOT NULL,
|
||||
city_id INTEGER NOT NULL,
|
||||
name_en TEXT NOT NULL,
|
||||
description_en TEXT NOT NULL,
|
||||
name_local TEXT NOT NULL,
|
||||
description_local TEXT NOT NULL,
|
||||
FOREIGN KEY(location_id) REFERENCES locations(id) ON DELETE CASCADE
|
||||
postal_code TEXT NOT NULL,
|
||||
FOREIGN KEY(city_id) REFERENCES cities(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_breweries_location_id ON breweries(location_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_breweries_city_id ON breweries(city_id);
|
||||
|
||||
)sql";
|
||||
|
||||
@@ -54,7 +55,7 @@ inline constexpr std::string_view kCreateUsersTableSql = R"sql(
|
||||
|
||||
CREATE TABLE IF NOT EXISTS users (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
location_id INTEGER NOT NULL,
|
||||
city_id INTEGER NOT NULL,
|
||||
first_name TEXT NOT NULL,
|
||||
last_name TEXT NOT NULL,
|
||||
gender TEXT NOT NULL,
|
||||
@@ -63,15 +64,15 @@ CREATE TABLE IF NOT EXISTS users (
|
||||
activity_weight REAL NOT NULL,
|
||||
email TEXT NOT NULL UNIQUE,
|
||||
date_of_birth TEXT NOT NULL,
|
||||
FOREIGN KEY(location_id) REFERENCES locations(id) ON DELETE CASCADE
|
||||
FOREIGN KEY(city_id) REFERENCES cities(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_users_location_id ON users(location_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_users_city_id ON users(city_id);
|
||||
|
||||
)sql";
|
||||
|
||||
inline constexpr std::string_view kInsertLocationSql = R"sql(
|
||||
INSERT INTO locations (
|
||||
inline constexpr std::string_view kInsertCitySql = R"sql(
|
||||
INSERT INTO cities (
|
||||
city,
|
||||
state_province,
|
||||
iso3166_2,
|
||||
@@ -85,17 +86,18 @@ INSERT INTO locations (
|
||||
|
||||
inline constexpr std::string_view kInsertBrewerySql = R"sql(
|
||||
INSERT INTO breweries (
|
||||
location_id,
|
||||
city_id,
|
||||
name_en,
|
||||
description_en,
|
||||
name_local,
|
||||
description_local
|
||||
) VALUES (?, ?, ?, ?, ?);
|
||||
description_local,
|
||||
postal_code
|
||||
) VALUES (?, ?, ?, ?, ?, ?);
|
||||
)sql";
|
||||
|
||||
inline constexpr std::string_view kInsertUserSql = R"sql(
|
||||
INSERT INTO users (
|
||||
location_id,
|
||||
city_id,
|
||||
first_name,
|
||||
last_name,
|
||||
gender,
|
||||
@@ -109,27 +111,28 @@ INSERT INTO users (
|
||||
|
||||
// sqlite3_bind_*() parameter indices are 1-based, matching the "?"
|
||||
// placeholder order in the SQL above.
|
||||
enum LocationBindIndex {
|
||||
kLocationCityBindIndex = 1,
|
||||
kLocationStateProvinceBindIndex,
|
||||
kLocationIso31662BindIndex,
|
||||
kLocationCountryBindIndex,
|
||||
kLocationIso31661BindIndex,
|
||||
kLocationLanguagesBindIndex,
|
||||
kLocationLatitudeBindIndex,
|
||||
kLocationLongitudeBindIndex,
|
||||
enum CityBindIndex {
|
||||
kCityNameBindIndex = 1,
|
||||
kCityStateProvinceBindIndex,
|
||||
kCityIso31662BindIndex,
|
||||
kCityCountryBindIndex,
|
||||
kCityIso31661BindIndex,
|
||||
kCityLanguagesBindIndex,
|
||||
kCityLatitudeBindIndex,
|
||||
kCityLongitudeBindIndex,
|
||||
};
|
||||
|
||||
enum BreweryBindIndex {
|
||||
kBreweryLocationIdBindIndex = 1,
|
||||
kBreweryCityIdBindIndex = 1,
|
||||
kBreweryEnglishNameBindIndex,
|
||||
kBreweryEnglishDescriptionBindIndex,
|
||||
kBreweryLocalNameBindIndex,
|
||||
kBreweryLocalDescriptionBindIndex,
|
||||
kBreweryPostalCodeBindIndex,
|
||||
};
|
||||
|
||||
enum UserBindIndex {
|
||||
kUserLocationIdBindIndex = 1,
|
||||
kUserCityIdBindIndex = 1,
|
||||
kUserFirstNameBindIndex,
|
||||
kUserLastNameBindIndex,
|
||||
kUserGenderBindIndex,
|
||||
|
||||
@@ -23,7 +23,7 @@ class IEnrichmentService {
|
||||
* @param loc Location to enrich.
|
||||
* @return Context text, or an empty string if unavailable.
|
||||
*/
|
||||
virtual std::string GetLocationContext(const Location& loc) = 0;
|
||||
virtual std::string GetLocationContext(const City& loc) = 0;
|
||||
};
|
||||
|
||||
#endif // BIERGARTEN_PIPELINE_INCLUDES_SERVICES_ENRICHMENT_ENRICHMENT_SERVICE_H_
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
*/
|
||||
class MockEnrichmentService final : public IEnrichmentService {
|
||||
public:
|
||||
std::string GetLocationContext(const Location& /*loc*/) override {
|
||||
std::string GetLocationContext(const City& /*loc*/) override {
|
||||
return {};
|
||||
}
|
||||
};
|
||||
|
||||
@@ -29,7 +29,7 @@ class WikipediaEnrichmentService final : public IEnrichmentService {
|
||||
/**
|
||||
* @brief Returns the Wikipedia-derived context for a location.
|
||||
*/
|
||||
[[nodiscard]] std::string GetLocationContext(const Location& loc) override;
|
||||
[[nodiscard]] std::string GetLocationContext(const City& loc) override;
|
||||
|
||||
private:
|
||||
std::string FetchExtract(std::string_view query);
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
#ifndef BIERGARTEN_PIPELINE_INCLUDES_SERVICES_POSTAL_CODE_MOCK_POSTAL_CODE_SERVICE_H_
|
||||
#define BIERGARTEN_PIPELINE_INCLUDES_SERVICES_POSTAL_CODE_MOCK_POSTAL_CODE_SERVICE_H_
|
||||
|
||||
/**
|
||||
* @file services/postal_code/mock_postal_code_service.h
|
||||
* @brief Placeholder IPostalCodeService used until xeger-based generation is
|
||||
* implemented.
|
||||
*/
|
||||
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
|
||||
#include "data_model/models.h"
|
||||
#include "services/postal_code/postal_code_service.h"
|
||||
|
||||
/**
|
||||
* @brief Postal code service that returns the location's first known
|
||||
* example postal code rather than generating one from its regex.
|
||||
*/
|
||||
class MockPostalCodeService final : public IPostalCodeService {
|
||||
public:
|
||||
std::string GeneratePostalCode(const City& location) override {
|
||||
if (location.postal_code_examples.empty()) {
|
||||
throw std::runtime_error(
|
||||
"MockPostalCodeService: location has no postal_code_examples: " +
|
||||
location.city);
|
||||
}
|
||||
return location.postal_code_examples.front();
|
||||
}
|
||||
};
|
||||
|
||||
#endif // BIERGARTEN_PIPELINE_INCLUDES_SERVICES_POSTAL_CODE_MOCK_POSTAL_CODE_SERVICE_H_
|
||||
@@ -0,0 +1,30 @@
|
||||
#ifndef BIERGARTEN_PIPELINE_INCLUDES_SERVICES_POSTAL_CODE_POSTAL_CODE_SERVICE_H_
|
||||
#define BIERGARTEN_PIPELINE_INCLUDES_SERVICES_POSTAL_CODE_POSTAL_CODE_SERVICE_H_
|
||||
|
||||
/**
|
||||
* @file services/postal_code/postal_code_service.h
|
||||
* @brief Abstraction for generating a postal code for a location.
|
||||
*/
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "data_model/models.h"
|
||||
|
||||
/**
|
||||
* @brief Interface for services that generate a postal code string matching
|
||||
* a location's postal code format (`City::postal_regex`).
|
||||
*/
|
||||
class IPostalCodeService {
|
||||
public:
|
||||
virtual ~IPostalCodeService() = default;
|
||||
|
||||
/**
|
||||
* @brief Generates a postal code for the given location.
|
||||
*
|
||||
* @param location Location whose postal code format to generate for.
|
||||
* @return A postal code string matching `location.postal_regex`.
|
||||
*/
|
||||
virtual std::string GeneratePostalCode(const City& location) = 0;
|
||||
};
|
||||
|
||||
#endif // BIERGARTEN_PIPELINE_INCLUDES_SERVICES_POSTAL_CODE_POSTAL_CODE_SERVICE_H_
|
||||
Reference in New Issue
Block a user