Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mapsme/omim.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorArsentiy Milchakov <milcars@mapswithme.com>2017-06-20 13:04:38 +0300
committerArsentiy Milchakov <milcars@mapswithme.com>2017-06-20 13:04:38 +0300
commit7471d5acb83e7dcf06783b3467607d2200de4bff (patch)
tree337c6bcc73cdcfe6158303f78800a7ad82ff48a3 /partners_api
parentdf51b0c0e83cbd312f14cdf727f15975464fb52e (diff)
[partners_api] viator
Diffstat (limited to 'partners_api')
-rw-r--r--partners_api/CMakeLists.txt2
-rw-r--r--partners_api/partners_api.pro2
-rw-r--r--partners_api/partners_api_tests/CMakeLists.txt1
-rw-r--r--partners_api/partners_api_tests/partners_api_tests.pro1
-rw-r--r--partners_api/partners_api_tests/viator_tests.cpp77
-rw-r--r--partners_api/viator_api.cpp245
-rw-r--r--partners_api/viator_api.hpp46
7 files changed, 374 insertions, 0 deletions
diff --git a/partners_api/CMakeLists.txt b/partners_api/CMakeLists.txt
index 91cbc42813..9d0ecd6a56 100644
--- a/partners_api/CMakeLists.txt
+++ b/partners_api/CMakeLists.txt
@@ -21,6 +21,8 @@ set(
rb_ads.hpp
uber_api.cpp
uber_api.hpp
+ viator_api.cpp
+ viator_api.hpp
)
add_library(${PROJECT_NAME} ${SRC})
diff --git a/partners_api/partners_api.pro b/partners_api/partners_api.pro
index 61e2fe682a..178ffbf797 100644
--- a/partners_api/partners_api.pro
+++ b/partners_api/partners_api.pro
@@ -17,6 +17,7 @@ SOURCES += \
opentable_api.cpp \
rb_ads.cpp \
uber_api.cpp \
+ viator_api.cpp \
HEADERS += \
ads_base.hpp \
@@ -28,3 +29,4 @@ HEADERS += \
opentable_api.hpp \
rb_ads.hpp \
uber_api.hpp \
+ viator_api.hpp \
diff --git a/partners_api/partners_api_tests/CMakeLists.txt b/partners_api/partners_api_tests/CMakeLists.txt
index 2978e32cd5..4fe9c140d4 100644
--- a/partners_api/partners_api_tests/CMakeLists.txt
+++ b/partners_api/partners_api_tests/CMakeLists.txt
@@ -10,6 +10,7 @@ set(
mopub_tests.cpp
rb_tests.cpp
uber_tests.cpp
+ viator_tests.cpp
)
omim_add_test(${PROJECT_NAME} ${SRC})
diff --git a/partners_api/partners_api_tests/partners_api_tests.pro b/partners_api/partners_api_tests/partners_api_tests.pro
index 7b6e225e38..c819af6212 100644
--- a/partners_api/partners_api_tests/partners_api_tests.pro
+++ b/partners_api/partners_api_tests/partners_api_tests.pro
@@ -32,3 +32,4 @@ SOURCES += \
mopub_tests.cpp \
rb_tests.cpp \
uber_tests.cpp \
+ viator_tests.cpp \
diff --git a/partners_api/partners_api_tests/viator_tests.cpp b/partners_api/partners_api_tests/viator_tests.cpp
new file mode 100644
index 0000000000..6a0bf9eed4
--- /dev/null
+++ b/partners_api/partners_api_tests/viator_tests.cpp
@@ -0,0 +1,77 @@
+#include "testing/testing.hpp"
+
+#include "partners_api/viator_api.hpp"
+
+#include <algorithm>
+#include <random>
+#include <sstream>
+#include <vector>
+
+#include "3party/jansson/myjansson.hpp"
+
+namespace
+{
+UNIT_TEST(Viator_GetTopProducts)
+{
+ string result;
+ viator::RawApi::GetTopProducts("684", "USD", 5, result);
+
+ TEST(!result.empty(), ());
+
+ my::Json root(result.c_str());
+ bool success;
+ FromJSONObjectOptionalField(root.get(), "success", success, false);
+ TEST(success, ());
+}
+
+UNIT_TEST(Viator_GetTop5Products)
+{
+ viator::Api api;
+ std::string const kSofia = "5630";
+
+ api.GetTop5Products(
+ kSofia, "",
+ [kSofia](std::string const & destId, std::vector<viator::Product> const & products) {
+ TEST_EQUAL(destId, kSofia, ());
+ TEST(!products.empty(), ());
+
+ testing::StopEventLoop();
+ });
+
+ testing::RunEventLoop();
+}
+
+UNIT_TEST(Viator_SortProducts)
+{
+ std::vector<viator::Product> products =
+ {
+ {"1", 10.0, 10, "", 10.0, "", "", "", ""},
+ {"2", 9.0, 100, "", 200.0, "", "", "", ""},
+ {"3", 9.0, 100, "", 100.0, "", "", "", ""},
+ {"4", 9.0, 10, "", 200.0, "", "", "", ""},
+ {"5", 9.0, 9, "", 300.0, "", "", "", ""},
+ {"6", 8.0, 200, "", 400.0, "", "", "", ""},
+ {"7", 7.0, 20, "", 200.0, "", "", "", ""},
+ {"8", 7.0, 20, "", 1.0, "", "", "", ""},
+ {"9", 7.0, 0, "", 300.0, "", "", "", ""},
+ {"10", 7.0, 0, "", 1.0, "", "", "", ""}
+ };
+
+ for (size_t i = 0 ; i < 1000; ++i)
+ {
+ std::shuffle(products.begin(), products.end(), std::minstd_rand(std::minstd_rand::default_seed));
+ viator::SortProducts(products);
+
+ TEST_EQUAL(products[0].m_title, "1", ());
+ TEST_EQUAL(products[1].m_title, "2", ());
+ TEST_EQUAL(products[2].m_title, "3", ());
+ TEST_EQUAL(products[3].m_title, "4", ());
+ TEST_EQUAL(products[4].m_title, "5", ());
+ TEST_EQUAL(products[5].m_title, "6", ());
+ TEST_EQUAL(products[6].m_title, "7", ());
+ TEST_EQUAL(products[7].m_title, "8", ());
+ TEST_EQUAL(products[8].m_title, "9", ());
+ TEST_EQUAL(products[9].m_title, "10", ());
+ }
+}
+} // namespace
diff --git a/partners_api/viator_api.cpp b/partners_api/viator_api.cpp
new file mode 100644
index 0000000000..a08fe9c275
--- /dev/null
+++ b/partners_api/viator_api.cpp
@@ -0,0 +1,245 @@
+#include "partners_api/viator_api.hpp"
+
+#include "platform/http_client.hpp"
+#include "platform/preferred_languages.hpp"
+
+#include "coding/multilang_utf8_string.hpp"
+
+#include "base/logging.hpp"
+#include "base/thread.hpp"
+
+#include <algorithm>
+#include <set>
+#include <sstream>
+#include <unordered_map>
+
+#include "3party/jansson/myjansson.hpp"
+
+#include "private.h"
+
+namespace
+{
+using namespace platform;
+using namespace viator;
+
+std::string const kApiUrl = "https://viatorapi.viator.com";
+std::string const kWebUrl = "https://www.partner.viator.com";
+
+int8_t GetLang(string const & lang)
+{
+ return StringUtf8Multilang::GetLangIndex(lang);
+}
+
+using IdsMap = std::unordered_map<int8_t, std::string>;
+
+IdsMap kApiKeys =
+{
+ {GetLang("en"), VIATOR_API_KEY_EN},
+ {GetLang("de"), VIATOR_API_KEY_DE},
+ {GetLang("fr"), VIATOR_API_KEY_FR},
+ {GetLang("es"), VIATOR_API_KEY_ES},
+ {GetLang("pt"), VIATOR_API_KEY_PT},
+ {GetLang("it"), VIATOR_API_KEY_IT},
+ {GetLang("nl"), VIATOR_API_KEY_NL},
+ {GetLang("sv"), VIATOR_API_KEY_SV},
+ {GetLang("ja"), VIATOR_API_KEY_JA}
+};
+
+IdsMap kAccountIds =
+{
+ {GetLang("en"), VIATOR_ACCOUNT_ID_EN},
+ {GetLang("de"), VIATOR_ACCOUNT_ID_DE},
+ {GetLang("fr"), VIATOR_ACCOUNT_ID_FR},
+ {GetLang("es"), VIATOR_ACCOUNT_ID_ES},
+ {GetLang("pt"), VIATOR_ACCOUNT_ID_PT},
+ {GetLang("it"), VIATOR_ACCOUNT_ID_IT},
+ {GetLang("nl"), VIATOR_ACCOUNT_ID_NL},
+ {GetLang("sv"), VIATOR_ACCOUNT_ID_SV},
+ {GetLang("ja"), VIATOR_ACCOUNT_ID_JA}
+};
+
+std::string GetId(IdsMap const & from)
+{
+ int8_t lang = GetLang(languages::GetCurrentNorm());
+
+ auto const it = from.find(lang);
+
+ if (it != from.cend())
+ return it->second;
+
+ LOG(LINFO, ("Viator key for language", lang, "is not found, English key will be used."));
+ return from.at(StringUtf8Multilang::kEnglishCode);
+}
+
+std::string GetApiKey()
+{
+ return GetId(kApiKeys);
+}
+
+std::string GetAccountId()
+{
+ return GetId(kAccountIds);
+}
+
+bool RunSimpleHttpRequest(string const & url, std::string const & bodyData, string & result)
+{
+ HttpClient request(url);
+ request.SetHttpMethod("POST");
+
+ request.SetBodyData(bodyData, "application/json");
+ if (request.RunHttpRequest() && !request.WasRedirected() && request.ErrorCode() == 200)
+ {
+ result = request.ServerResponse();
+ return true;
+ }
+
+ return false;
+}
+
+std::string MakeSearchProductsRequest(int destId, std::string const & currency, int count)
+{
+ std::ostringstream os;
+ // REVIEW_AVG_RATING_D - average traveler rating (high->low).
+ os << R"({"topX":"1-)" << count << R"(","destId":)" << destId << R"(,"currencyCode":")"
+ << currency << R"(","sortOrder":"REVIEW_AVG_RATING_D"})";
+
+ return os.str();
+}
+
+std::string MakeUrl(std::string const & apiMethod)
+{
+ std::ostringstream os;
+ os << kApiUrl << apiMethod << "?apiKey=" << GetApiKey();
+
+ return os.str();
+}
+
+bool CheckAnswer(my::Json const & root)
+{
+ bool success;
+ FromJSONObjectOptionalField(root.get(), "success", success, false);
+
+ if (!success)
+ {
+ std::string errorMessage;
+ FromJSONObject(root.get(), "errorMessageText", errorMessage);
+ LOG(LWARNING, ("Viator retrieved unsuccessfull status, error message:", errorMessage));
+ return false;
+ }
+
+ return true;
+}
+
+bool CheckDataArray(json_t const * data)
+{
+ if (data == nullptr)
+ return false;
+
+ if (!json_is_array(data))
+ return false;
+
+ if (json_array_size(data) <= 0)
+ return false;
+
+ return true;
+}
+
+void MakeProducts(std::string const & src, std::vector<Product> & products)
+{
+ products.clear();
+
+ my::Json root(src.c_str());
+ auto const data = json_object_get(root.get(), "data");
+ if (!CheckAnswer(root) || !CheckDataArray(data))
+ return;
+
+ auto const dataSize = json_array_size(data);
+ for (size_t i = 0; i < dataSize; ++i)
+ {
+ Product product;
+ auto const item = json_array_get(data, i);
+ FromJSONObject(item, "shortTitle", product.m_title);
+ FromJSONObject(item, "rating", product.m_rating);
+ FromJSONObject(item, "reviewCount", product.m_reviewCount);
+ FromJSONObject(item, "duration", product.m_duration);
+ FromJSONObject(item, "price", product.m_price);
+ FromJSONObject(item, "priceFormatted", product.m_priceFormatted);
+ FromJSONObject(item, "currencyCode", product.m_currency);
+ FromJSONObject(item, "thumbnailHiResURL", product.m_photoUrl);
+ FromJSONObject(item, "webURL", product.m_pageUrl);
+ products.push_back(move(product));
+ }
+}
+} // namespace
+
+namespace viator
+{
+// static
+bool RawApi::GetTopProducts(string const & destId, string const & currency, int count,
+ string & result)
+{
+ int dest = 0;
+ CHECK(strings::to_int(destId, dest), ());
+
+ return RunSimpleHttpRequest(MakeUrl("/service/search/products"),
+ MakeSearchProductsRequest(dest, currency, count), result);
+}
+
+// static
+std::string Api::GetCityUrl(std::string const & destId, std::string const & name)
+{
+ std::ostringstream ost;
+ // The final language and city name will be calculated automatically based on account id and
+ // destination id.
+ ost << kWebUrl << "/" << languages::GetCurrentNorm() << "/" << GetAccountId() << "/" << name
+ << "/d" << destId << "-ttd?activities=all";
+ return ost.str();
+}
+
+void Api::GetTop5Products(std::string const & destId, std::string const & currency,
+ GetTop5ProductsCallback const & fn) const
+{
+ std::string curr = currency.empty() ? "USD" : currency;
+
+ threads::SimpleThread([destId, curr, fn]()
+ {
+ string result;
+ if (!RawApi::GetTopProducts(destId, curr, 5, result))
+ return fn(destId, {});
+
+ std::vector<Product> products;
+ try
+ {
+ MakeProducts(result, products);
+ }
+ catch (my::Json::Exception const & e)
+ {
+ LOG(LERROR, (e.Msg()));
+ products.clear();
+ }
+
+ SortProducts(products);
+
+ fn(destId, products);
+ }).detach();
+}
+
+bool operator<(Product const & lhs, Product const & rhs)
+{
+ return lhs.m_rating < rhs.m_rating ||
+ (lhs.m_rating == rhs.m_rating && lhs.m_reviewCount < rhs.m_reviewCount) ||
+ (lhs.m_reviewCount == rhs.m_reviewCount && lhs.m_price < rhs.m_price);
+}
+
+// Sort by rating (from the best to the worst),
+// then by reviews (from the largest to the smallest),
+// then by price (from the biggest to the lowest)
+void SortProducts(std::vector<Product> & products)
+{
+ std::multiset<Product> productsSet;
+ for (auto const & p : products)
+ productsSet.insert(p);
+
+ std::copy(productsSet.crbegin(), productsSet.crend(), products.begin());
+}
+} // namespace viator
diff --git a/partners_api/viator_api.hpp b/partners_api/viator_api.hpp
new file mode 100644
index 0000000000..1c2edc4986
--- /dev/null
+++ b/partners_api/viator_api.hpp
@@ -0,0 +1,46 @@
+#pragma once
+
+#include <functional>
+#include <string>
+
+namespace viator
+{
+class RawApi
+{
+public:
+ /// Returns top <count> products for specified city id.
+ static bool GetTopProducts(std::string const & destId, std::string const & currency, int count,
+ std::string & result);
+};
+
+struct Product
+{
+ std::string m_title;
+ double m_rating;
+ int m_reviewCount;
+ std::string m_duration;
+ double m_price;
+ std::string m_priceFormatted;
+ std::string m_currency;
+ std::string m_photoUrl;
+ std::string m_pageUrl;
+};
+
+using GetTop5ProductsCallback =
+ std::function<void(std::string const & destId, std::vector<Product> const & products)>;
+
+class Api
+{
+public:
+ /// Returns web page address for specified city id.
+ static std::string GetCityUrl(std::string const & destId, std::string const & name);
+
+ /// Returns top-5 products for specified city id.
+ /// @currency - currency of the price, if empty then USD will be used.
+ void GetTop5Products(std::string const & destId, std::string const & currency,
+ GetTop5ProductsCallback const & fn) const;
+};
+
+bool operator<(Product const & lhs, Product const & rhs);
+void SortProducts(std::vector<Product> & products);
+} // namespace viator