23 #include <boost/process/environment.hpp> 32 #include <boost/version.hpp> 33 #if BOOST_VERSION < 107600 35 #define BOOST_BIND_GLOBAL_PLACEHOLDERS 37 #include <boost/algorithm/string.hpp> 38 #include <boost/filesystem.hpp> 39 #include <boost/version.hpp> 40 #if BOOST_VERSION < 108600 41 #include <boost/process.hpp> 42 #ifdef BOOST_WINDOWS_API 43 #include <boost/process/windows.hpp> 46 #include <boost/process/v1/async.hpp> 47 #include <boost/process/v1/child.hpp> 48 #include <boost/process/v1/env.hpp> 49 #include <boost/process/v1/environment.hpp> 50 #include <boost/process/v1/error.hpp> 51 #include <boost/process/v1/group.hpp> 52 #include <boost/process/v1/io.hpp> 53 #include <boost/process/v1/pipe.hpp> 54 #include <boost/process/v1/search_path.hpp> 55 #include <boost/process/v1/start_dir.hpp> 56 #ifdef BOOST_WINDOWS_API 57 #include <boost/process/v1/windows.hpp> 60 #include <boost/regex.hpp> 61 #include <boost/property_tree/ptree.hpp> 62 #include <boost/property_tree/json_parser.hpp> 63 #include <boost/iostreams/device/array.hpp> 64 #include <boost/iostreams/stream_buffer.hpp> 65 #include <boost/locale.hpp> 66 #include <boost/asio.hpp> 69 #include <gnc-datetime.hpp> 70 #include <gnc-numeric.hpp> 71 #include "gnc-quotes.hpp" 77 #include <gnc-session.h> 81 static const QofLogModule log_module =
"gnc.price-quotes";
82 static const char* av_api_env =
"ALPHAVANTAGE_API_KEY";
83 static const char* av_api_key =
"alphavantage-api-key";
84 static const char* yh_api_env =
"FINANCEAPI_API_KEY";
85 static const char* yh_api_key =
"yhfinance-api-key";
87 namespace bl = boost::locale;
88 #if BOOST_VERSION < 108800 89 namespace bp = boost::process;
91 namespace bp = boost::process::v1;
93 namespace bfs = boost::filesystem;
94 namespace bpt = boost::property_tree;
95 namespace bio = boost::iostreams;
97 using QuoteResult = std::tuple<int, StrVec, StrVec>;
105 gnc_quotes_get_quotable_commodities(
const gnc_commodity_table *
table);
111 virtual const StrVec& get_sources()
const noexcept = 0;
112 virtual const std::string & get_version()
const noexcept = 0;
113 virtual QuoteResult
get_quotes(
const std::string& json_str)
const = 0;
125 void fetch (QofBook *book);
126 void fetch (CommVec& commodities);
127 void fetch (gnc_commodity *comm);
128 void report (
const char* source,
const StrVec& commodities,
bool verbose);
130 const std::string& version() noexcept {
return m_quotesource->get_version(); }
131 const QuoteSources& sources() noexcept {
return m_sources; }
132 bool had_failures() noexcept {
return !m_failures.empty(); }
133 const QFVec& failures() noexcept;
134 std::string report_failures() noexcept;
137 std::string query_fq (
const char* source,
const StrVec& commoditites);
138 std::string query_fq (
const CommVec&);
139 bpt::ptree parse_quotes (
const std::string& quote_str);
140 void create_quotes(
const bpt::ptree& pt,
const CommVec& comm_vec);
141 std::string comm_vec_to_json_string(
const CommVec&)
const;
142 GNCPrice* parse_one_quote(
const bpt::ptree&, gnc_commodity*);
144 std::unique_ptr<GncQuoteSource> m_quotesource;
145 QuoteSources m_sources;
148 gnc_commodity *m_dflt_curr;
153 const bfs::path c_cmd;
154 std::string c_fq_wrapper;
155 std::string m_version;
157 bp::environment m_env;
161 const std::string& get_version()
const noexcept
override {
return m_version; }
162 const StrVec& get_sources()
const noexcept
override {
return m_sources; }
163 QuoteResult
get_quotes(
const std::string&)
const override;
165 QuoteResult run_cmd (
const StrVec& args,
const std::string& json_string)
const;
166 void set_api_key(
const char* api_pref,
const char* api_env);
169 static void show_quotes(
const bpt::ptree& pt,
const StrVec& commodities,
bool verbose);
170 static void show_currency_quotes(
const bpt::ptree& pt,
const StrVec& commodities,
bool verbose);
171 static std::string parse_quotesource_error(
const std::string& line);
173 static const std::string empty_string{};
175 GncFQQuoteSource::GncFQQuoteSource() :
176 c_cmd{bp::search_path(
"perl")},
177 m_version{}, m_sources{}, m_env{boost::this_process::environment()}
179 char *bindir = gnc_path_get_bindir();
180 c_fq_wrapper = std::string(bindir) +
"/finance-quote-wrapper";
182 StrVec args{
"-w", c_fq_wrapper,
"-v"};
183 auto [rv, sources, errors] = run_cmd(args, empty_string);
186 std::string err{bl::translate(
"Failed to initialize Finance::Quote: ")};
187 for (
const auto& err_line : errors)
188 err += err_line.empty() ?
"" : err_line +
"\n";
193 std::string err{bl::translate(
"Finance::Quote check returned error ")};
194 for(
const auto& err_line : errors)
195 err += err.empty() ?
"" : err_line +
"\n";
198 auto version{sources.front()};
201 std::string err{bl::translate(
"No Finance::Quote Version")};
204 m_version = std::move(version);
205 sources.erase(sources.begin());
206 m_sources = std::move(sources);
207 std::sort (m_sources.begin(), m_sources.end());
209 set_api_key(av_api_key, av_api_env);
210 set_api_key(yh_api_key, yh_api_env);
214 GncFQQuoteSource::get_quotes(
const std::string& json_str)
const 216 StrVec args{
"-w", c_fq_wrapper,
"-f" };
217 return run_cmd(args, json_str);
221 GncFQQuoteSource::run_cmd (
const StrVec& args,
const std::string& json_string)
const 223 StrVec out_vec, err_vec;
228 std::future<std::vector<char> > out_buf, err_buf;
229 boost::asio::io_context svc;
231 auto input_buf = bp::buffer (json_string);
233 process = bp::child(c_cmd, args,
234 bp::std_out > out_buf,
235 bp::std_err > err_buf,
236 bp::std_in < input_buf,
237 #ifdef BOOST_WINDOWS_API
238 bp::windows::create_no_window,
247 auto raw = out_buf.get();
248 std::vector<std::string> data;
250 bio::stream_buffer<bio::array_source> sb(raw.data(), raw.size());
251 std::istream is(&sb);
253 while (std::getline(is, line) && !line.empty())
256 if (line.back() ==
'\r')
259 out_vec.push_back (std::move(line));
262 bio::stream_buffer<bio::array_source> eb(raw.data(), raw.size());
263 std::istream es(&eb);
265 while (std::getline(es, line) && !line.empty())
266 err_vec.push_back (std::move(line));
268 cmd_result = process.exit_code();
270 catch (std::exception &e)
273 err_vec.push_back(e.what());
276 return QuoteResult (cmd_result, std::move(out_vec), std::move(err_vec));
280 GncFQQuoteSource::set_api_key(
const char* api_key,
const char* api_env)
285 m_env[api_env] = key;
290 if (api_key == av_api_key && m_env.find(api_env) == m_env.end())
291 PWARN(
"No Alpha Vantage API key set, currency quotes and other " 292 "AlphaVantage based quotes won't work.");
299 m_sources{}, m_failures{},
303 m_sources = m_quotesource->get_sources();
306 GncQuotesImpl::GncQuotesImpl(QofBook* book) : m_quotesource{
new GncFQQuoteSource},
307 m_sources{}, m_book{book},
310 m_sources = m_quotesource->get_sources();
313 GncQuotesImpl::GncQuotesImpl(QofBook* book, std::unique_ptr<GncQuoteSource> quote_source) :
314 m_quotesource{std::move(quote_source)},
317 m_sources = m_quotesource->get_sources();
321 GncQuotesImpl::fetch (QofBook *book)
324 throw (
GncQuoteException(bl::translate(
"GncQuotes::Fetch called with no book.")));
325 auto commodities = gnc_quotes_get_quotable_commodities (
331 GncQuotesImpl::fetch (gnc_commodity *comm)
333 auto commodities = CommVec {comm};
338 GncQuotesImpl::fetch (CommVec& commodities)
341 if (commodities.empty())
342 throw (
GncQuoteException(bl::translate(
"GncQuotes::Fetch called with no commodities.")));
343 auto quote_str{query_fq (commodities)};
344 auto ptree{parse_quotes (quote_str)};
345 create_quotes(ptree, commodities);
349 GncQuotesImpl::report (
const char* source,
const StrVec& commodities,
353 throw (
GncQuoteException(bl::translate(
"GncQuotes::Report called with no source.")));
355 bool is_currency{strcmp(source,
"currency") == 0};
357 if (commodities.empty())
359 std::cerr << _(
"There were no commodities for which to retrieve quotes.") << std::endl;
364 auto quote_str{query_fq (source, commodities)};
365 auto ptree{parse_quotes (quote_str)};
366 auto source_pt_ai{ptree.find(source)};
368 show_currency_quotes(source_pt_ai->second, commodities, verbose);
370 show_quotes(source_pt_ai->second, commodities, verbose);
374 std::cerr << _(
"Finance::Quote retrieval failed with error ") << err.what() << std::endl;
379 GncQuotesImpl::failures() noexcept
385 explain(GncQuoteError err,
const std::string& errmsg)
390 case GncQuoteError::NO_RESULT:
392 retval += _(
"Finance::Quote returned no data and set no error.");
394 retval += _(
"Finance::Quote returned an error: ") + errmsg;
396 case GncQuoteError::QUOTE_FAILED:
398 retval += _(
"Finance::Quote reported failure set no error.");
400 retval += _(
"Finance::Quote reported failure with error: ") + errmsg;
402 case GncQuoteError::NO_CURRENCY:
403 retval += _(
"Finance::Quote returned a quote with no currency.");
405 case GncQuoteError::UNKNOWN_CURRENCY:
406 retval += _(
"Finance::Quote returned a quote with a currency GnuCash doesn't know about.");
408 case GncQuoteError::NO_PRICE:
409 retval += _(
"Finance::Quote returned a quote with no price element.");
411 case GncQuoteError::PRICE_PARSE_FAILURE:
412 retval += _(
"Finance::Quote returned a quote with a price that GnuCash was unable to covert to a number.");
414 case GncQuoteError::SUCCESS:
416 retval += _(
"The quote has no error set.");
423 GncQuotesImpl::report_failures() noexcept
425 std::string retval{_(
"Quotes for the following commodities were unavailable or unusable:\n")};
426 std::for_each(m_failures.begin(), m_failures.end(),
427 [&retval](
auto failure)
429 auto [ns, sym, reason, err] = failure;
430 retval +=
"* " + ns +
":" + sym +
" " +
431 explain(reason, err) +
"\n";
438 using Path = bpt::ptree::path_type;
439 static inline Path make_quote_path(
const std::string &name_space,
440 const std::string &symbol)
442 using Path = bpt::ptree::path_type;
443 Path key{name_space,
'|'};
444 key /= Path{symbol,
'|'};
449 GncQuotesImpl::comm_vec_to_json_string(
const CommVec &comm_vec)
const 451 bpt::ptree pt, pt_child;
454 std::for_each (comm_vec.cbegin(), comm_vec.cend(),
455 [
this, &pt] (
auto comm)
458 auto comm_ns = std::string(
"currency");
462 (!comm_mnemonic || (strcmp(comm_mnemonic,
"XXX") == 0)))
468 pt.put (make_quote_path(comm_ns, comm_mnemonic),
"");
472 std::ostringstream result;
473 bpt::write_json(result, pt);
477 static inline std::string
478 get_quotes(
const std::string& json_str,
const std::unique_ptr<GncQuoteSource>& qs)
480 auto [rv, quotes, errors] = qs->get_quotes(json_str);
485 for (
const auto& line : quotes)
486 answer.append(line +
"\n");
491 for (
const auto& line: errors)
493 if (line ==
"invalid_json\n")
494 PERR(
"Finanace Quote Wrapper was unable to parse %s",
496 err_str += parse_quotesource_error(line);
505 GncQuotesImpl::query_fq (
const char* source,
const StrVec& commodities)
508 auto is_currency{strcmp(source,
"currency") == 0};
510 if (is_currency && commodities.size() < 2)
514 pt.put(
"defaultcurrency", commodities[0].c_str());
518 std::for_each(is_currency ? ++commodities.cbegin() : commodities.cbegin(),
520 [source, &pt](
auto sym)
522 pt.put(make_quote_path(source, sym),
"");
524 std::ostringstream result;
525 bpt::write_json(result, pt);
526 auto result_str{result.str()};
527 PINFO(
"Query JSON: %s\n", result_str.c_str());
528 return get_quotes(result.str(), m_quotesource);
532 GncQuotesImpl::query_fq (
const CommVec& comm_vec)
534 auto json_str{comm_vec_to_json_string(comm_vec)};
535 PINFO(
"Query JSON: %s\n", json_str.c_str());
542 const char* mnemonic;
545 boost::optional<std::string> price;
547 boost::optional<std::string> date;
548 boost::optional<std::string> time;
549 boost::optional<std::string> currency;
550 boost::optional<std::string> errormsg;
554 get_price_and_type(
PriceParams& p,
const bpt::ptree& comm_pt)
557 p.price = comm_pt.get_optional<std::string> (p.type);
562 p.price = comm_pt.get_optional<std::string> (p.type);
568 p.price = comm_pt.get_optional<std::string> (p.type);
571 p.type = p.price ?
"unknown" :
"missing";
576 parse_quote_json(
PriceParams& p,
const bpt::ptree& comm_pt)
578 auto success = comm_pt.get_optional<
bool> (
"success");
579 p.success = success && *success;
581 p.errormsg = comm_pt.get_optional<std::string> (
"errormsg");
582 get_price_and_type(p, comm_pt);
583 auto inverted = comm_pt.get_optional<
bool> (
"inverted");
584 p.inverted = inverted && *inverted;
585 p.date = comm_pt.get_optional<std::string> (
"date");
586 p.time = comm_pt.get_optional<std::string> (
"time");
587 p.currency = comm_pt.get_optional<std::string> (
"currency");
590 PINFO(
"Commodity: %s", p.mnemonic);
591 PINFO(
" Success: %s", (p.success ?
"yes" :
"no"));
592 PINFO(
" Date: %s", (p.date ? p.date->c_str() :
"missing"));
593 PINFO(
" Time: %s", (p.time ? p.time->c_str() :
"missing"));
594 PINFO(
" Currency: %s", (p.currency ? p.currency->c_str() :
"missing"));
595 PINFO(
" Price: %s", (p.price ? p.price->c_str() :
"missing"));
596 PINFO(
" Inverted: %s\n", (p.inverted ?
"yes" :
"no"));
608 if (p.date && !p.date->empty())
613 PINFO(
"Quote date included, using %s for %s:%s",
614 quote_time.format(
"%Y-%m-%d %H:%M:%S %z").c_str(), p.ns, p.mnemonic);
615 return static_cast<time64>(quote_time);
617 catch (
const std::exception &err)
620 PWARN(
"Warning: failed to parse quote date '%s' for %s:%s because %s - will use %s",
621 p.date->c_str(), p.ns, p.mnemonic, err.what(), now.format(
"%Y-%m-%d %H:%M:%S %z").c_str());
622 return static_cast<time64>(now);
627 PINFO(
"No date was returned for %s:%s - will use %s",
628 p.ns, p.mnemonic, now.format(
"%Y-%m-%d %H:%M:%S %z").c_str());
629 return static_cast<time64>(now);
632 static boost::optional<GncNumeric>
635 boost::optional<GncNumeric> price;
642 PWARN(
"Skipped %s:%s - failed to parse returned price '%s'",
643 p.ns, p.mnemonic, p.price->c_str());
646 if (price && p.inverted)
647 *price = price->inv();
652 static gnc_commodity*
653 get_currency(
const PriceParams& p, QofBook* book, QFVec& failures)
657 failures.emplace_back(p.ns, p.mnemonic, GncQuoteError::NO_CURRENCY,
659 PWARN(
"Skipped %s:%s - Finance::Quote returned a quote with no currency",
663 std::string curr_str = *p.currency;
664 boost::to_upper (curr_str);
666 auto currency = gnc_commodity_table_lookup (commodity_table,
"ISO4217", curr_str.c_str());
670 failures.emplace_back(p.ns, p.mnemonic,
671 GncQuoteError::UNKNOWN_CURRENCY, empty_string);
672 PWARN(
"Skipped %s:%s - failed to parse returned currency '%s'",
673 p.ns, p.mnemonic, p.currency->c_str());
681 GncQuotesImpl::parse_one_quote(
const bpt::ptree& pt, gnc_commodity* comm)
689 (!p.mnemonic || (strcmp (p.mnemonic,
"XXX") == 0)))
692 auto source_pt_ai{pt.find(source)};
693 auto ok{source_pt_ai != pt.not_found()};
696 auto comm_pt_ai{source_pt_ai->second.find(p.mnemonic)};
697 ok = (comm_pt_ai != pt.not_found());
699 comm_pt = comm_pt_ai->second;
703 m_failures.emplace_back(p.ns, p.mnemonic, GncQuoteError::NO_RESULT,
705 PINFO(
"Skipped %s:%s - Finance::Quote didn't return any data from %s.",
706 p.ns, p.mnemonic, source);
710 parse_quote_json(p, comm_pt);
713 m_failures.emplace_back(p.ns, p.mnemonic, GncQuoteError::QUOTE_FAILED,
714 p.errormsg ? *p.errormsg : empty_string);
715 PWARN(
"Skipped %s:%s - Finance::Quote returned fetch failure.\nReason %s",
717 (p.errormsg ? p.errormsg->c_str() :
"unknown"));
723 m_failures.emplace_back(p.ns, p.mnemonic,
724 GncQuoteError::NO_PRICE, empty_string);
725 PWARN(
"Skipped %s:%s - Finance::Quote didn't return a valid price",
730 auto price{get_price(p)};
733 m_failures.emplace_back(p.ns, p.mnemonic,
734 GncQuoteError::PRICE_PARSE_FAILURE,
739 auto currency{get_currency(p, m_book, m_failures)};
743 auto quotedt{calc_price_time(p)};
745 gnc_price_begin_edit (gnc_price);
746 gnc_price_set_commodity (gnc_price, comm);
747 gnc_price_set_currency (gnc_price, currency);
748 gnc_price_set_time64 (gnc_price, static_cast<time64> (quotedt));
749 gnc_price_set_source (gnc_price, PRICE_SOURCE_FQ);
750 gnc_price_set_typestr (gnc_price, p.type.c_str());
751 gnc_price_set_value (gnc_price, *price);
752 gnc_price_commit_edit (gnc_price);
757 GncQuotesImpl::parse_quotes (
const std::string& quote_str)
760 std::istringstream ss {quote_str};
765 bpt::read_json (ss, pt);
767 catch (bpt::json_parser_error &e) {
770 catch (
const std::runtime_error& e)
774 catch (
const std::logic_error& e)
779 std::string error_msg{_(
"Failed to parse result returned by Finance::Quote.")};
782 error_msg += _(
"Result:");
784 error_msg += quote_str;
789 std::string error_msg{_(
"Failed to parse result returned by Finance::Quote.")};
792 error_msg += _(
"Error message:");
797 error_msg += _(
"Result:");
799 error_msg += quote_str;
806 GncQuotesImpl::create_quotes (
const bpt::ptree& pt,
const CommVec& comm_vec)
809 for (
auto comm : comm_vec)
811 auto price{parse_one_quote(pt, comm)};
820 show_verbose_quote(
const bpt::ptree& comm_pt)
822 std::for_each(comm_pt.begin(), comm_pt.end(),
824 std::cout << std::setw(12) << std::right << elem.first <<
" => " <<
825 std::left << elem.second.data() <<
"\n";
827 std::cout << std::endl;
831 show_gnucash_quote(
const bpt::ptree& comm_pt)
833 constexpr
const char* ptr{
"<=== "};
834 constexpr
const char* dptr{
"<=\\ "};
835 constexpr
const char* uptr{
"<=/ "};
837 const char* reqd{C_(
"Finance::Quote",
"required")};
839 const char* rec{C_(
"Finance::Quote",
"recommended")};
841 const char* oot{C_(
"Finance::Quote",
"one of these")};
843 const char* miss{C_(
"Finance::Quote",
"**missing**")};
845 const std::string miss_str{miss};
846 auto outline{[](
const char* label, std::string value,
const char* pointer,
const char* req) {
847 std::cout << std::setw(12) << std::right << label << std::setw(16) << std::left <<
848 value << pointer << req <<
"\n";
850 std::cout << _(
"Finance::Quote fields GnuCash uses:") <<
"\n";
852 outline(C_(
"Finance::Quote",
"symbol: "), comm_pt.get<
char>(
"symbol", miss), ptr, reqd);
854 outline(C_(
"Finance::Quote",
"date: "), comm_pt.get<
char>(
"date", miss), ptr, rec);
856 outline(C_(
"Finance::Quote",
"currency: "), comm_pt.get<
char>(
"currency", miss), ptr, reqd);
857 auto last{comm_pt.get<
char>(
"last",
"")};
858 auto nav{comm_pt.get<
char>(
"nav",
"")};
859 auto price{comm_pt.get<
char>(
"nav",
"")};
860 auto no_price{last.empty() && nav.empty() && price.empty()};
862 outline(C_(
"Finance::Quote",
"last: "), no_price ? miss_str : last, dptr,
"");
864 outline(C_(
"Finance::Quote",
"nav: "), no_price ? miss_str : nav, ptr, oot);
866 outline(C_(
"Finance::Quote",
"price: "), no_price ? miss_str : price, uptr,
"");
867 std::cout << std::endl;
869 static const bpt::ptree empty_tree{};
871 static inline const bpt::ptree&
872 get_commodity_data(
const bpt::ptree& pt,
const std::string& comm)
874 auto commdata{pt.find(comm)};
875 if (commdata == pt.not_found())
877 std::cout << comm <<
" " << _(
"Finance::Quote returned no data and set no error.") << std::endl;
880 auto& comm_pt{commdata->second};
881 auto success = comm_pt.get_optional<
bool> (
"success");
882 if (!(success && *success))
884 auto errormsg = comm_pt.get_optional<std::string> (
"errormsg");
885 if (errormsg && !errormsg->empty())
886 std::cout << _(
"Finance::Quote reported a failure for symbol ") <<
887 comm <<
": " << *errormsg << std::endl;
889 std::cout << _(
"Finance::Quote failed silently to retrieve a quote for symbol ") <<
897 show_quotes(
const bpt::ptree& pt,
const StrVec& commodities,
bool verbose)
899 for (
const auto& comm : commodities)
901 auto comm_pt{get_commodity_data(pt, comm)};
903 if (comm_pt == empty_tree)
908 std::cout << comm <<
":\n";
909 show_verbose_quote(comm_pt);
913 show_gnucash_quote(comm_pt);
919 show_currency_quotes(
const bpt::ptree& pt,
const StrVec& commodities,
bool verbose)
921 auto to_cur{commodities.front()};
922 for (
const auto& comm : commodities)
927 auto comm_pt{get_commodity_data(pt, comm)};
929 if (comm_pt == empty_tree)
934 std::cout << comm <<
":\n";
935 show_verbose_quote(comm_pt);
939 std::cout <<
"1 " << comm <<
" = " <<
940 comm_pt.get<
char>(
"last",
"Not Found") <<
" " << to_cur <<
"\n";
942 std::cout << std::endl;
947 parse_quotesource_error(
const std::string& line)
950 if (line ==
"invalid_json\n")
952 err_str += _(
"GnuCash submitted invalid json to Finance::Quote. The details were logged.");
954 else if (line.substr(0, 15) ==
"missing_modules")
956 PERR(
"Missing Finance::Quote Dependencies: %s",
957 line.substr(17).c_str());
958 err_str += _(
"Perl is missing the following modules. Please see https://wiki.gnucash.org/wiki/Online_Quotes#Finance::Quote for detailed corrective action. ");
959 err_str += line.substr(17);
963 PERR(
"Unrecognized Finance::Quote Error %s", line.c_str());
964 err_str +=_(
"Unrecognized Finance::Quote Error: ");
979 get_quotables_helper1 (gpointer value, gpointer data)
981 auto l =
static_cast<CommVec *
> (data);
982 auto comm =
static_cast<gnc_commodity *
> (value);
988 !quote_source || !quote_source_supported)
995 get_quotables_helper2 (gnc_commodity *comm, gpointer data)
997 auto l =
static_cast<CommVec *
> (data);
1003 !quote_source || !quote_source_supported)
1005 l->push_back (comm);
1010 gnc_quotes_get_quotable_commodities (
const gnc_commodity_table *
table)
1012 gnc_commodity_namespace * ns = NULL;
1013 const char *name_space;
1014 GList * nslist, * tmp;
1017 const char *expression = gnc_prefs_get_namespace_regexp ();
1023 if (expression && *expression)
1025 if (regcomp (&pattern, expression, REG_EXTENDED | REG_ICASE) != 0)
1032 for (tmp = nslist; tmp; tmp = tmp->next)
1034 name_space =
static_cast<const char *
> (tmp->data);
1035 if (regexec (&pattern, name_space, 0, NULL, 0) == 0)
1042 g_list_foreach (cm_list, &get_quotables_helper1, (gpointer) &l);
1043 g_list_free (cm_list);
1047 g_list_free (nslist);
1065 m_impl = std::make_unique<GncQuotesImpl>();
1075 m_impl->fetch (book);
1080 m_impl->fetch (commodities);
1085 m_impl->fetch (comm);
1091 m_impl->report(source, commodities, verbose);
1096 return m_impl->version ();
1101 return m_impl->sources ();
1104 GncQuotes::~GncQuotes() =
default;
1109 return m_impl->had_failures();
1115 return m_impl->failures();
1119 GncQuotes::report_failures() noexcept
1121 return m_impl->report_failures();
GNCPrice * gnc_price_create(QofBook *book)
gnc_price_create - returns a newly allocated and initialized price with a reference count of 1...
gboolean gnc_commodity_table_foreach_commodity(const gnc_commodity_table *table, gboolean(*f)(gnc_commodity *cm, gpointer user_data), gpointer user_data)
Call a function once for each commodity in the commodity table.
gnc_commodity_table * gnc_commodity_table_get_table(QofBook *book)
Returns the commodity table associated with a book.
gboolean gnc_commodity_is_currency(const gnc_commodity *cm)
Checks to see if the specified commodity is an ISO 4217 recognized currency or a legacy currency...
gchar * gnc_prefs_get_string(const gchar *group, const gchar *pref_name)
Get a string value from the preferences backend.
const char * gnc_commodity_get_mnemonic(const gnc_commodity *cm)
Retrieve the mnemonic for the specified commodity.
utility functions for the GnuCash UI
#define PINFO(format, args...)
Print an informational note.
gboolean gnc_commodity_get_quote_flag(const gnc_commodity *cm)
Retrieve the automatic price quote flag for the specified commodity.
Commodity handling public routines (C++ api)
gboolean gnc_pricedb_add_price(GNCPriceDB *db, GNCPrice *p)
Add a price to the pricedb.
gboolean gnc_quote_source_get_supported(const gnc_quote_source *source)
Given a gnc_quote_source data structure, return the flag that indicates whether this particular quote...
bool had_failures() noexcept
Report if there were quotes requested but not retrieved.
The primary numeric class for representing amounts and values.
const char * gnc_commodity_get_namespace(const gnc_commodity *cm)
Retrieve the namespace for the specified commodity.
#define PERR(format, args...)
Log a serious error.
GList * gnc_commodity_namespace_get_commodity_list(const gnc_commodity_namespace *name_space)
Return a list of all commodity data structures in the specified namespace.
GNCPriceDB * gnc_pricedb_get_db(QofBook *book)
Return the pricedb associated with the book.
gnc_commodity * gnc_default_currency(void)
Return the default currency set by the user.
#define PWARN(format, args...)
Log a warning.
QofBook * qof_session_get_book(const QofSession *session)
Returns the QofBook of this session.
GList * gnc_commodity_table_get_namespaces(const gnc_commodity_table *table)
Return a list of all namespaces in the commodity table.
const QFVec & failures() noexcept
Report the commodities for which quotes were requested but not successfully retrieved.
GncQuotes()
Create a GncQuotes object.
Encapsulate all the information about a dataset.
Generic api to store and retrieve preferences.
gnc_quote_source * gnc_commodity_get_quote_source(const gnc_commodity *cm)
Retrieve the automatic price quote source for the specified commodity.
gnc_commodity_namespace * gnc_commodity_table_find_namespace(const gnc_commodity_table *table, const char *name_space)
This function finds a commodity namespace in the set of existing commodity namespaces.
void report(const char *source, const StrVec &commodities, bool verbose=false)
Report quote results from Finance::Quote to std::cout.
void fetch(QofBook *book)
Fetch quotes for all commodities in our db that have a quote source set.
gint64 time64
Most systems that are currently maintained, including Microsoft Windows, BSD-derived Unixes and Linux...
const char * gnc_quote_source_get_internal_name(const gnc_quote_source *source)
Given a gnc_quote_source data structure, return the internal name of this quote source.
const std::string & version() noexcept
Get the installed Finance::Quote version.
const QuoteSources & sources() noexcept
Get the available Finance::Quote sources as a std::vector.
Commodity handling public routines.
gboolean gnc_commodity_equiv(const gnc_commodity *a, const gnc_commodity *b)
This routine returns TRUE if the two commodities are equivalent.