Update generate pipeline to add OpenAI api as an additional backend (for comparison testing), add brewery coordinates (#244)

* add brewery coordinates

* Add openai mode

* Add coordinates to schema

* Fix C++ review findings in pipeline: k-prefixed enum values, include ordering, populated validation error messages, conventional const notation, and endl replaced with newline
This commit is contained in:
2026-08-23 15:49:28 -04:00
committed by Aaron Po
parent 31da1d557c
commit e738fe484a
35 changed files with 1327 additions and 343 deletions

View File

@@ -4,12 +4,55 @@
*/
#include <chrono>
#include <cmath>
#include <format>
#include <numbers>
#include <optional>
#include <random>
#include "biergarten_pipeline_orchestrator.h"
#include "services/logging/logger.h"
struct Coords {
double longitude;
double latitude;
};
// @todo find a better home for this
namespace {
constexpr double kEarthRadiusKm = 6371.0;
double DegreesToRadians(double degrees) {
return degrees * std::numbers::pi / 180.0;
}
double RadiansToDegrees(double radians) {
return radians * 180.0 / std::numbers::pi;
}
} // namespace
static Coords GetRandomCoordsWithinRange(const Coords& centre,
const int distance_km,
std::mt19937& rng) {
std::uniform_real_distribution<double> angle_dist(0.0,
2.0 * std::numbers::pi);
std::uniform_real_distribution<double> unit_dist(0.0, 1.0);
const double angle_radians = angle_dist(rng);
// sqrt() keeps points uniformly distributed over the disc's area rather
// than clustering near the centre.
const double radius_km = distance_km * std::sqrt(unit_dist(rng));
const double delta_latitude_degrees =
RadiansToDegrees(radius_km * std::cos(angle_radians) / kEarthRadiusKm);
const double delta_longitude_degrees = RadiansToDegrees(
radius_km * std::sin(angle_radians) /
(kEarthRadiusKm * std::cos(DegreesToRadians(centre.latitude))));
return Coords{.longitude = centre.longitude + delta_longitude_degrees,
.latitude = centre.latitude + delta_latitude_degrees};
}
void BiergartenPipelineOrchestrator::GenerateBreweries(
std::span<const EnrichedCity> cities) {
logger_->Log({.level = LogLevel::Info,
@@ -19,18 +62,28 @@ void BiergartenPipelineOrchestrator::GenerateBreweries(
generated_breweries_.clear();
size_t skipped_count = 0;
size_t export_failed_count = 0;
std::mt19937 rng(std::random_device{}());
const auto generate_record =
[this, &skipped_count](
[this, &skipped_count, &rng](
const EnrichedCity& enriched_city) -> std::optional<BreweryRecord> {
try {
const BreweryResult brewery =
generator_->GenerateBrewery(enriched_city);
const std::string postal_code =
postal_code_service_->GeneratePostalCode(enriched_city.location);
constexpr int kMaxDistanceFromCentreKm = 5;
const Coords city_centre{.longitude = enriched_city.location.longitude,
.latitude = enriched_city.location.latitude};
const Coords brewery_coords = GetRandomCoordsWithinRange(
city_centre, kMaxDistanceFromCentreKm, rng);
return BreweryRecord{
.address = BreweryAddress{.city = enriched_city.location,
.postal_code = postal_code},
.postal_code = postal_code,
.longitude = brewery_coords.longitude,
.latitude = brewery_coords.latitude},
.brewery = brewery};
} catch (const std::exception& e) {
++skipped_count;
@@ -91,4 +144,4 @@ void BiergartenPipelineOrchestrator::GenerateBreweries(
"brewery/breweries to SQLite",
export_failed_count)});
}
}
}

View File

@@ -17,17 +17,19 @@ void BiergartenPipelineOrchestrator::LogResults() const {
for (const auto& [address, brewery] : generated_breweries_) {
const City& city = address.city;
brewery_output.push_back(boost::json::object{
{"name_en", brewery.name_en},
{"description_en", brewery.description_en},
{"name_local", brewery.name_local},
{"description_local", brewery.description_local},
{"address", boost::json::object{
{"city", city.city},
{"country", city.country},
{"state_province", city.state_province},
{"iso3166_2", city.iso3166_2},
{"postal_code", address.postal_code},
}}});
{"name_en", brewery.name_en},
{"description_en", brewery.description_en},
{"name_local", brewery.name_local},
{"description_local", brewery.description_local},
{"address", boost::json::object{
{"city", city.city},
{"country", city.country},
{"state_province", city.state_province},
{"iso3166_2", city.iso3166_2},
{"postal_code", address.postal_code},
{"longitude", address.longitude},
{"latitude", address.latitude},
}}});
}
std::ostringstream brewery_oss;
@@ -40,14 +42,14 @@ void BiergartenPipelineOrchestrator::LogResults() const {
for (const auto& generated_user : generated_users_) {
user_output.push_back(boost::json::object{
{"first_name", generated_user.user.first_name},
{"last_name", generated_user.user.last_name},
{"gender", generated_user.user.gender},
{"username", generated_user.user.username},
{"bio", generated_user.user.bio},
{"activity_weight", generated_user.user.activity_weight},
{"email", generated_user.email},
{"date_of_birth", generated_user.date_of_birth},
{"first_name", generated_user.user.first_name},
{"last_name", generated_user.user.last_name},
{"gender", generated_user.user.gender},
{"username", generated_user.user.username},
{"bio", generated_user.user.bio},
{"activity_weight", generated_user.user.activity_weight},
{"email", generated_user.email},
{"date_of_birth", generated_user.date_of_birth},
});
}
@@ -56,4 +58,4 @@ void BiergartenPipelineOrchestrator::LogResults() const {
logger_->Log({.level = LogLevel::Info,
.phase = PipelinePhase::Teardown,
.message = user_oss.str()});
}
}

View File

@@ -39,11 +39,10 @@ std::vector<City> BiergartenPipelineOrchestrator::QueryLocations() {
logger_->Log({.level = LogLevel::Info,
.phase = PipelinePhase::Startup,
.message = std::format(" Locations available: {}",
all_locations.size())});
logger_->Log({.level = LogLevel::Info,
.phase = PipelinePhase::Startup,
.message = std::format(" Sampled locations: {}",
sampled_locations.size())});
.message = std::format(
" Locations available: {}\n Sampled Locations: {}",
all_locations.size(), sampled_locations.size())});
return sampled_locations;
}
}