From 59a8fdbf56463957e5886feb8e78bfbc483a43ed Mon Sep 17 00:00:00 2001 From: Dan Paulat Date: Sat, 1 Feb 2025 22:32:39 -0600 Subject: [PATCH] List NWS text products metadata --- .../scwx/provider/iem_warnings_provider.hpp | 6 + .../scwx/provider/iem_warnings_provider.cpp | 123 ++++++++++++++++++ 2 files changed, 129 insertions(+) diff --git a/wxdata/include/scwx/provider/iem_warnings_provider.hpp b/wxdata/include/scwx/provider/iem_warnings_provider.hpp index 64c5e96f..2061afa4 100644 --- a/wxdata/include/scwx/provider/iem_warnings_provider.hpp +++ b/wxdata/include/scwx/provider/iem_warnings_provider.hpp @@ -1,6 +1,7 @@ #pragma once #include +#include namespace scwx::provider { @@ -20,6 +21,11 @@ public: IemWarningsProvider(IemWarningsProvider&&) noexcept; IemWarningsProvider& operator=(IemWarningsProvider&&) noexcept; + std::vector + ListTextProducts(std::chrono::sys_time date, + std::optional cccc = {}, + std::optional pil = {}); + private: class Impl; std::unique_ptr p; diff --git a/wxdata/source/scwx/provider/iem_warnings_provider.cpp b/wxdata/source/scwx/provider/iem_warnings_provider.cpp index 7c780816..b40955ca 100644 --- a/wxdata/source/scwx/provider/iem_warnings_provider.cpp +++ b/wxdata/source/scwx/provider/iem_warnings_provider.cpp @@ -1,12 +1,23 @@ #include +#include +#include +#include #include +#include +#include + namespace scwx::provider { static const std::string logPrefix_ = "scwx::provider::iem_warnings_provider"; static const auto logger_ = util::Logger::Create(logPrefix_); +static const std::string kBaseUrl_ = "https://mesonet.agron.iastate.edu/api/1"; + +static const std::string kListNwsTextProductsEndpoint_ = "/nws/afos/list.json"; +static const std::string kNwsTextProductEndpoint_ = "/nwstext/"; + class IemWarningsProvider::Impl { public: @@ -16,6 +27,11 @@ public: Impl& operator=(const Impl&) = delete; Impl(const Impl&&) = delete; Impl& operator=(const Impl&&) = delete; + + std::vector + ListTextProducts(std::chrono::sys_time date, + std::optional cccc = {}, + std::optional pil = {}); }; IemWarningsProvider::IemWarningsProvider() : p(std::make_unique()) {} @@ -26,4 +42,111 @@ IemWarningsProvider::IemWarningsProvider(IemWarningsProvider&&) noexcept = IemWarningsProvider& IemWarningsProvider::operator=(IemWarningsProvider&&) noexcept = default; +std::vector IemWarningsProvider::ListTextProducts( + std::chrono::sys_time date, + std::optional cccc, + std::optional pil) +{ + return p->ListTextProducts(date, cccc, pil); +} + +std::vector IemWarningsProvider::Impl::ListTextProducts( + std::chrono::sys_time date, + std::optional cccc, + std::optional pil) +{ + using namespace std::chrono; + +#if (__cpp_lib_chrono >= 201907L) + namespace df = std; + + static constexpr std::string_view kDateFormat {"{:%Y-%m-%d}"}; +#else + using namespace date; + namespace df = date; + +# define kDateFormat "%Y-%m-%d" +#endif + + auto parameters = cpr::Parameters {{"date", df::format(kDateFormat, date)}}; + + // WMO Source Code + if (cccc.has_value()) + { + parameters.Add({"cccc", std::string {cccc.value()}}); + } + + // AFOS / AWIPS ID / 3-6 length identifier + if (pil.has_value()) + { + parameters.Add({"pil", std::string {pil.value()}}); + } + + auto response = + cpr::Get(cpr::Url {kBaseUrl_ + kListNwsTextProductsEndpoint_}, + network::cpr::GetHeader(), + parameters); + boost::json::value json = util::json::ReadJsonString(response.text); + + std::vector textProducts {}; + + if (response.status_code == cpr::status::HTTP_OK) + { + try + { + // Get AFOS list from response + auto entries = boost::json::value_to(json); + + for (auto& entry : entries.data_) + { + textProducts.push_back(entry.productId_); + } + + logger_->trace("Found {} products", entries.data_.size()); + } + catch (const std::exception& ex) + { + // Unexpected bad response + logger_->warn("Error parsing JSON: {}", ex.what()); + } + } + else if (response.status_code == cpr::status::HTTP_BAD_REQUEST && + json != nullptr) + { + try + { + // Log bad request details + auto badRequest = boost::json::value_to(json); + logger_->warn("ListTextProducts bad request: {}", badRequest.detail_); + } + catch (const std::exception& ex) + { + // Unexpected bad response + logger_->warn("Error parsing bad response: {}", ex.what()); + } + } + else if (response.status_code == cpr::status::HTTP_UNPROCESSABLE_ENTITY && + json != nullptr) + { + try + { + // Log validation error details + auto error = boost::json::value_to(json); + logger_->warn("ListTextProducts validation error: {}", + error.detail_.at(0).msg_); + } + catch (const std::exception& ex) + { + // Unexpected bad response + logger_->warn("Error parsing validation error: {}", ex.what()); + } + } + else + { + logger_->warn("Could not list text products: {}", response.status_line); + } + + return textProducts; +} + } // namespace scwx::provider