From 341403bbe2ebe89e50202c3ca34caeb747ea153a Mon Sep 17 00:00:00 2001 From: Arsentiy Milchakov Date: Wed, 14 Oct 2020 12:30:51 +0300 Subject: [PATCH] [taxi][core] citymobil taxi is added --- data/taxi_places/citymobil.json | 78 ++++++ data/taxi_places/yandex.json | 44 ---- partners_api/CMakeLists.txt | 2 + partners_api/citymobil_api.cpp | 198 +++++++++++++++ partners_api/citymobil_api.hpp | 91 +++++++ .../partners_api_tests/CMakeLists.txt | 1 + .../partners_api_tests/citymobil_tests.cpp | 71 ++++++ .../partners_api_tests/taxi_engine_tests.cpp | 49 +++- partners_api/taxi_engine.cpp | 2 + partners_api/taxi_places_loader.cpp | 1 + partners_api/taxi_provider.hpp | 4 +- tools/python/ResponseProvider.py | 8 + tools/python/jsons.py | 229 ++++++++++++++++++ 13 files changed, 731 insertions(+), 47 deletions(-) create mode 100644 data/taxi_places/citymobil.json create mode 100644 partners_api/citymobil_api.cpp create mode 100644 partners_api/citymobil_api.hpp create mode 100644 partners_api/partners_api_tests/citymobil_tests.cpp diff --git a/data/taxi_places/citymobil.json b/data/taxi_places/citymobil.json new file mode 100644 index 0000000000..2904b45d60 --- /dev/null +++ b/data/taxi_places/citymobil.json @@ -0,0 +1,78 @@ +{ + "disabled": { + "countries": [], + "mwms": [] + }, + "enabled": { + "countries": [ + { + "cities": [ + "Balakovo", + "Berdsk", + "Chelyabinsk", + "Dmitrov", + "Dolgoprudny", + "Dzerzhinsk", + "Elektrostal", + "Engels", + "Engels", + "Kazan", + "Khimki", + "Klin", + "Kolomna", + "Krasnodar", + "Krasnogorsk", + "Krasnoyarsk", + "Moscow", + "Mytishchi", + "Nizhny Novgorod", + "Noginsk", + "Novosibirsk", + "Odintsovo", + "Omsk", + "Pavlovsky Posad", + "Perm", + "Reutov", + "Rostov", + "Saint Petersburg", + "Samara", + "Saratov", + "Sergiyev Posad", + "Serpukhov", + "Sochi", + "Solnechnogorsk", + "Tolyatti", + "Tyumen", + "Ufa", + "Ulyanovsk", + "Vidnoye", + "Volgograd", + "Voronezh", + "Voskresensk", + "Yaroslavl", + "Yekaterinburg", + "\u041e\u0440\u0435\u0445\u043e\u0432\u043e-\u0417\u0443\u0435\u0432\u043e", + "Lobnya", + "Domodedovo", + "Zelenograd", + "Korolyov", + "Lyubertsy", + "Pushkino", + "Skhodnya", + "Balashikha", + "Shchyolkovo", + "Ivanteyevka", + "Podolsk", + "Zhukovsky", + "Ramenskoye", + "Krasnoobsk", + "Akademgorodok", + "Bataysk", + "Kopeysk" + ], + "id": "Russian Federation" + } + ], + "mwms": [] + } +} \ No newline at end of file diff --git a/data/taxi_places/yandex.json b/data/taxi_places/yandex.json index d50d72c77d..e52793e179 100644 --- a/data/taxi_places/yandex.json +++ b/data/taxi_places/yandex.json @@ -107,7 +107,6 @@ "\u0422\u0430\u0440\u043a\u043e-\u0421\u0430\u043b\u0435", "\u0420\u044b\u0431\u043d\u043e\u0435", "\u0420\u0443\u0437\u0430\u0435\u0432\u043a\u0430", - "\u041e\u0440\u0435\u0445\u043e\u0432\u043e-\u0417\u0443\u0435\u0432\u043e", "Nyandoma", "Novopavlovsk", "\u041c\u0438\u0440\u043d\u044b\u0439", @@ -230,7 +229,6 @@ "Grozny", "\u0413\u0443\u0431\u043a\u0438\u043d\u0441\u043a\u0438\u0439", "Gusev", - "Dmitrov", "Dobryanka", "Drezna", "Dyurtyuli", @@ -307,7 +305,6 @@ "Svetlogorsk", "Svobodny", "\u0421\u0435\u043c\u0438\u0431\u0440\u0430\u0442\u043e\u0432\u043e", - "Sergiyev Posad", "Sibay", "Slavgorod", "Slavyansk-na-Kubani", @@ -333,7 +330,6 @@ "Sharypovo", "Shatura", "Shuya", - "Elektrostal", "Yugo-Kamskiy", "Yuzhno-Sakhalinsk", "Yartsevo", @@ -347,7 +343,6 @@ "Balashov", "Bashmakovo", "Belokurikha", - "Berdsk", "Birsk", "Bratsk", "Bryukhovetskaya", @@ -355,7 +350,6 @@ "Buynaksk", "Velikiye Luki", "Vorkuta", - "Voskresensk", "Vsevolozhsk", "Vyatskiye Polyany", "Gelendzhik", @@ -410,7 +404,6 @@ "Ozyorsk", "Orlovskiy", "Pavlovo", - "Pavlovsky Posad", "Pereslavl-Zalessky", "\u041f\u043e\u0439\u043a\u043e\u0432\u0441\u043a\u0438\u0439", "Priozersk", @@ -422,7 +415,6 @@ "Sarov", "Sverdlovskiy", "Serdobsk", - "Solnechnogorsk", "Stavropol", "Stary Oskol", "Surgut", @@ -463,7 +455,6 @@ "Atkarsk", "Baksan", "Balabanovo", - "Balakovo", "Belaya Glina", "Belgorod", "Belebei", @@ -511,11 +502,9 @@ "Karachayevsk", "Kimry", "Kineshma", - "Klin", "Kovrov", "Kovylkino", "Kozelsk", - "Kolomna", "Korenovsk", "Kotlas", "Krasnoufimsk", @@ -541,7 +530,6 @@ "Nizhnekamsk", "Nikolayevsk-on-Amur", "Novopokrovskaya", - "Noginsk", "Norilsk", "Noyabrsk", "Nyurba", @@ -560,7 +548,6 @@ "Rezh", "Rovenki", "Roslavl", - "Rostov", "Rubtsovsk", "Sasovo", "Severodvinsk", @@ -718,21 +705,16 @@ "Veliky Novgorod", "Vladivostok", "Vladimir", - "Volgograd", "Vologda", - "Voronezh", "Votkinsk", "Gatchina", - "Dzerzhinsk", "Dimitrovgrad", "Donskoj", - "Yekaterinburg", "Zlatoust", "Ivanovo", "Izhevsk", "Irkutsk", "Yoshkar-Ola", - "Kazan", "Kaliningrad", "Kaluga", "Kemerovo", @@ -740,8 +722,6 @@ "Kirov", "Kondrovo", "Kostroma", - "Krasnodar", - "Krasnoyarsk", "Kungur", "Kyzyl", "Lipetsk", @@ -750,73 +730,49 @@ "Magnitogorsk", "Miass", "Minusinsk", - "Moscow", "Murmansk", "Naberezhnye Chelny", - "Nizhny Novgorod", "Nizhny Tagil", "Novoaltaysk", "Novokuznetsk", "Novokuybyshevsk", "Novomoskovsk", "Novorossiysk", - "Novosibirsk", "Novocherkassk", "Obninsk", "Oktyabrskiy", - "Omsk", "Orenburg", "Orsk", "Penza", "Pervouralsk", - "Perm", "Petrozavodsk", "Pskov", "Rostov-on-Don", "Ryazan", "Salavat", - "Samara", - "Saint Petersburg", "Saransk", - "Saratov", "Sayanogorsk", - "Serpukhov", "Smolensk", "Solikamsk", - "Sochi", "Sterlitamak", "Syzran", "Syktyvkar", "Taganrog", "Tambov", "Tver", - "Tolyatti", "Tomsk", - "Tyumen", "Ulan-Ude", - "Ulyanovsk", - "Ufa", "Khabarovsk", "Chaikovsky", "Chapaevsk", "Cheboksary", - "Chelyabinsk", "Cherepovets", "Chernogorsk", "Chita", "Chusovoy", "Shakhty", - "Engels", - "Yaroslavl", - "Mytishchi", - "Reutov", "Kotelniki", "Dzerzhinsky", - "Vidnoye", - "Odintsovo", - "Krasnogorsk", - "Dolgoprudny", - "Khimki", "Sestroretsk", "Murino", "Shushary", diff --git a/partners_api/CMakeLists.txt b/partners_api/CMakeLists.txt index 1a69b6d30a..c7ad83b9ab 100644 --- a/partners_api/CMakeLists.txt +++ b/partners_api/CMakeLists.txt @@ -40,6 +40,8 @@ set( booking_ordering_params.cpp booking_ordering_params.hpp booking_params_base.hpp + citymobil_api.cpp + citymobil_api.hpp freenow_api.cpp freenow_api.hpp guides_on_map_api.cpp diff --git a/partners_api/citymobil_api.cpp b/partners_api/citymobil_api.cpp new file mode 100644 index 0000000000..8a24ffd840 --- /dev/null +++ b/partners_api/citymobil_api.cpp @@ -0,0 +1,198 @@ +#include "partners_api/citymobil_api.hpp" + +#include "platform/http_client.hpp" +#include "platform/platform.hpp" + +#include "geometry/latlon.hpp" + +#include "coding/url.hpp" +#include "coding/writer.hpp" + +#include "base/logging.hpp" +#include "base/thread.hpp" + +#include "std/target_os.hpp" + +#include +#include +#include + +#include "3party/jansson/myjansson.hpp" + +#include "private.h" + +namespace +{ +bool RunHttpRequest(std::string const & url, std::string && body, std::string & result) +{ + platform::HttpClient request(url); + request.SetRawHeader("Accept", "application/json"); + request.SetRawHeader("Authorization", CITYMOBIL_TOKEN); + request.SetBodyData(std::move(body), "application/json"); + if (request.RunHttpRequest() && !request.WasRedirected() && request.ErrorCode() == 200) + { + result = request.ServerResponse(); + return true; + } + + return false; +} + +template +std::string SerializeToJson(T const & data) +{ + std::string jsonStr; + using Sink = MemWriter; + Sink sink(jsonStr); + coding::SerializerJson serializer(sink); + serializer(data); + return jsonStr; +} +} // namespace + +namespace taxi +{ +namespace citymobil +{ +std::string const kBaseUrl = "https://t.city-mobil.ru/taxiserv/api/corporation/v2"; + +// static +bool RawApi::GetSupportedTariffs(SupportedTariffsBody const & body, std::string & result, + std::string const & baseUrl /* = kBaseUrl */) +{ + return RunHttpRequest(url::Join(baseUrl, "get_supported_tariffs"), SerializeToJson(body), result); +} + +// static +bool RawApi::CalculatePrice(CalculatePriceBody const & body, std::string & result, + std::string const & baseUrl /* = kBaseUrl */) +{ + return RunHttpRequest(url::Join(baseUrl, "calculate_price"), SerializeToJson(body), result); +} + +/// Requests list of available products from Citymobil. +void Api::GetAvailableProducts(ms::LatLon const & from, ms::LatLon const & to, + ProductsCallback const & successFn, + ErrorProviderCallback const & errorFn) +{ + ASSERT(successFn, ()); + ASSERT(errorFn, ()); + + // TODO(a): Add ErrorCode::FarDistance and provide this error code. + if (!IsDistanceSupported(from, to)) + { + errorFn(ErrorCode::NoProducts); + return; + } + + auto const baseUrl = m_baseUrl; + + GetPlatform().RunTask(Platform::Thread::Network, [from, to, baseUrl, successFn, errorFn]() + { + std::string tariffsResult; + RawApi::SupportedTariffsBody supportedTariffs(from); + if (!RawApi::GetSupportedTariffs(supportedTariffs, tariffsResult, baseUrl)) + { + errorFn(ErrorCode::RemoteError); + return; + } + + RawApi::TariffGroups tariffGroups = MakeTariffGroupsFromJson(tariffsResult); + + std::string calculatePriceResult; + RawApi::CalculatePriceBody calculatePrice(from, to, tariffGroups); + if (!RawApi::CalculatePrice(calculatePrice, calculatePriceResult, baseUrl)) + { + errorFn(ErrorCode::RemoteError); + return; + } + + std::vector products; + try + { + products = MakeProductsFromJson(calculatePriceResult); + } + catch (base::Json::Exception const & e) + { + LOG(LERROR, (e.Msg())); + products.clear(); + } + + if (products.empty()) + errorFn(ErrorCode::NoProducts); + else + successFn(products); + + }); +} + +/// Returns link which allows you to launch the Citymobil app. +RideRequestLinks Api::GetRideRequestLinks(std::string const & productId, ms::LatLon const & from, + ms::LatLon const & to) const +{ + std::ostringstream deepLink; + deepLink << std::fixed << std::setprecision(6) << "https://trk.mail.ru/c/q4akt6?from=" + << from.m_lat << "," << from.m_lon << "&to=" << to.m_lat << "," << to.m_lon + << "&tariff=" << productId; + + return {deepLink.str(), deepLink.str()}; +} + +RawApi::TariffGroups MakeTariffGroupsFromJson(std::string const & src) +{ + RawApi::TariffGroups result; + base::Json root(src.c_str()); + auto const tariffGroups = json_object_get(root.get(), "tariff_groups"); + auto const tariffGroupsSize = json_array_size(tariffGroups); + result.resize(tariffGroupsSize); + + for (size_t i = 0; i < tariffGroupsSize; ++i) + { + auto const item = json_array_get(tariffGroups, i); + FromJSONObject(item, "tariff_group_id", result[i]); + } + + return result; +} + +std::vector MakeProductsFromJson(std::string const & src) +{ + std::vector products; + + base::Json root(src.c_str()); + + uint32_t serviceStatus; + FromJSONObject(root.get(), "service_status", serviceStatus); + // Temporary unavailable. + if (serviceStatus == 0) + return {}; + + uint32_t eta = 0; + FromJSONObject(root.get(), "eta", eta); + + auto const productsArray = json_object_get(root.get(), "prices"); + auto const productsSize = json_array_size(productsArray); + for (size_t i = 0; i < productsSize; ++i) + { + taxi::Product product; + uint32_t id = 0; + uint32_t price = 0; + auto const item = json_array_get(productsArray, i); + + FromJSONObject(item, "id_tariff_group", id); + FromJSONObject(item, "total_price", price); + + auto const tariffInfo = json_object_get(item, "tariff_info"); + FromJSONObject(tariffInfo, "name", product.m_name); + + product.m_productId = strings::to_string(id); + product.m_price = strings::to_string(price); + product.m_time = strings::to_string(eta); + product.m_currency = "RUB"; + products.push_back(std::move(product)); + } + + return products; +} +} // namespace citymobil +} // namespace taxi diff --git a/partners_api/citymobil_api.hpp b/partners_api/citymobil_api.hpp new file mode 100644 index 0000000000..e06eb813e5 --- /dev/null +++ b/partners_api/citymobil_api.hpp @@ -0,0 +1,91 @@ +#pragma once + +#include "partners_api/taxi_base.hpp" + +#include "coding/serdes_json.hpp" + +#include +#include + +namespace ms +{ +class LatLon; +} + +namespace taxi +{ +namespace citymobil +{ +extern std::string const kBaseUrl; +/// Citymobil api wrapper based on synchronous http requests. +class RawApi +{ +public: + using TariffGroups = std::vector; + + struct Position + { + DECLARE_VISITOR(visitor(m_lat, "latitude"), visitor(m_lon, "longitude")); + + double m_lat = 0.0; + double m_lon = 0.0; + }; + + class CalculatePriceBody + { + public: + CalculatePriceBody(ms::LatLon const & from, ms::LatLon const & to, TariffGroups const & tariffs) + : m_pickup{from.m_lat, from.m_lon} + , m_dropoff{to.m_lat, to.m_lon} + , m_tariffGroups(tariffs) + { + } + + DECLARE_VISITOR(visitor(m_pickup, "pickup"), visitor(m_dropoff, "dropoff"), + visitor(m_tariffGroups, "tariff_group"), visitor(m_waypoints, "waypoints")); + + private: + Position m_pickup; + Position m_dropoff; + TariffGroups m_tariffGroups; + // Required api parameter but it is not used in our app. + std::vector m_waypoints; + }; + + class SupportedTariffsBody + { + public: + SupportedTariffsBody(ms::LatLon const & pos) : m_pickup{pos.m_lat, pos.m_lon} {} + + DECLARE_VISITOR(visitor(m_pickup, "pickup")); + + private: + Position m_pickup; + }; + + static bool GetSupportedTariffs(SupportedTariffsBody const & body, std::string & result, + std::string const & url = kBaseUrl); + static bool CalculatePrice(CalculatePriceBody const & body, std::string & result, + std::string const & url = kBaseUrl); +}; + +/// Class which used for making products from http requests results. +class Api : public ApiBase +{ +public: + explicit Api(std::string const & baseUrl = kBaseUrl) : ApiBase(baseUrl) {} + // ApiBase overrides: + /// Requests list of available products from Citymobil. + void GetAvailableProducts(ms::LatLon const & from, ms::LatLon const & to, + ProductsCallback const & successFn, + ErrorProviderCallback const & errorFn) override; + + /// Returns link which allows you to launch the Citymobil app. + RideRequestLinks GetRideRequestLinks(std::string const & productId, ms::LatLon const & from, + ms::LatLon const & to) const override; +}; + +RawApi::TariffGroups MakeTariffGroupsFromJson(std::string const & src); +std::vector MakeProductsFromJson(std::string const & src); +} // namespace citymobil +} // namespace taxi diff --git a/partners_api/partners_api_tests/CMakeLists.txt b/partners_api/partners_api_tests/CMakeLists.txt index 5d34b73c73..6c12730d71 100644 --- a/partners_api/partners_api_tests/CMakeLists.txt +++ b/partners_api/partners_api_tests/CMakeLists.txt @@ -7,6 +7,7 @@ set( ads_engine_tests.cpp booking_tests.cpp bookmark_catalog_ads_tests.cpp + citymobil_tests.cpp download_on_map_container_delegate.hpp facebook_tests.cpp freenow_tests.cpp diff --git a/partners_api/partners_api_tests/citymobil_tests.cpp b/partners_api/partners_api_tests/citymobil_tests.cpp new file mode 100644 index 0000000000..9a3b5b7820 --- /dev/null +++ b/partners_api/partners_api_tests/citymobil_tests.cpp @@ -0,0 +1,71 @@ +#include "testing/testing.hpp" + +#include "partners_api/citymobil_api.hpp" + +#include "geometry/latlon.hpp" + +#include "platform/platform.hpp" + +#include + +namespace +{ +using Runner = Platform::ThreadRunner; + +UNIT_TEST(Citymobil_GetSupportedTariffs) +{ + std::string result; + taxi::citymobil::RawApi::SupportedTariffsBody supportedTariffs({55.796918, 37.537859}); + taxi::citymobil::RawApi::GetSupportedTariffs(supportedTariffs, result); + + TEST(!result.empty(), ()); +} + +UNIT_TEST(Citymobil_CalculatePrice) +{ + std::string result; + taxi::citymobil::RawApi::CalculatePriceBody calculatePrice({55.796918, 37.537859}, + {55.758213, 37.616093}, + {1, 2, 3, 4, 5}); + taxi::citymobil::RawApi::CalculatePrice(calculatePrice, result); + + TEST(!result.empty(), ()); +} + +UNIT_CLASS_TEST(Runner, Citymobil_GetAvailableProducts) +{ + taxi::citymobil::Api api("http://localhost:34568/partners"); + ms::LatLon const from(55.796918, 37.537859); + ms::LatLon const to(55.758213, 37.616093); + + std::vector resultProducts; + + api.GetAvailableProducts(from, to, + [&resultProducts](std::vector const & products) { + resultProducts = products; + testing::Notify(); + }, + [](taxi::ErrorCode const code) { + TEST(false, (code)); + }); + + testing::Wait(); + + TEST(!resultProducts.empty(), ()); + + taxi::ErrorCode errorCode = taxi::ErrorCode::RemoteError; + ms::LatLon const farPos(56.838197, 35.908507); + api.GetAvailableProducts(from, farPos, + [](std::vector const & products) { + TEST(false, ()); + }, + [&errorCode](taxi::ErrorCode const code) { + errorCode = code; + testing::Notify(); + }); + + testing::Wait(); + + TEST_EQUAL(errorCode, taxi::ErrorCode::NoProducts, ()); +} +} // namespace diff --git a/partners_api/partners_api_tests/taxi_engine_tests.cpp b/partners_api/partners_api_tests/taxi_engine_tests.cpp index 99c7431e22..00961a47be 100644 --- a/partners_api/partners_api_tests/taxi_engine_tests.cpp +++ b/partners_api/partners_api_tests/taxi_engine_tests.cpp @@ -1,11 +1,13 @@ #include "testing/testing.hpp" +#include "partners_api/citymobil_api.hpp" #include "partners_api/freenow_api.hpp" #include "partners_api/maxim_api.hpp" #include "partners_api/rutaxi_api.hpp" #include "partners_api/taxi_engine.hpp" #include "partners_api/uber_api.hpp" #include "partners_api/yandex_api.hpp" +#include "partners_api/yango_api.hpp" #include "platform/platform_tests_support/async_gui_thread.hpp" @@ -110,6 +112,18 @@ public: storage::CountryId GetMwmId(m2::PointD const & point) override { return {}; } }; +class RussiaMoscowDelegate : public taxi::Delegate +{ +public: + storage::CountriesVec GetCountryIds(m2::PointD const & point) override + { + return {"Russian Federation"}; + } + + std::string GetCityName(m2::PointD const & point) override { return "Moscow"; } + storage::CountryId GetMwmId(m2::PointD const & point) override { return {}; } +}; + std::vector GetUberSynchronous(ms::LatLon const & from, ms::LatLon const & to, std::string const & url) { @@ -191,6 +205,25 @@ std::vector GetFreenowSynchronous(ms::LatLon const & from, ms::La return taxi::freenow::MakeProductsFromJson(freenowAnswer); } +std::vector GetCitymobilSynchronous(ms::LatLon const & from, ms::LatLon const & to, + std::string const & url) +{ + std::string tariffsResult; + taxi::citymobil::RawApi::SupportedTariffsBody supportedTariffs(from); + TEST(taxi::citymobil::RawApi::GetSupportedTariffs(supportedTariffs, tariffsResult, url), ()); + return {}; + + taxi::citymobil::RawApi::TariffGroups tariffGroups = + taxi::citymobil::MakeTariffGroupsFromJson(tariffsResult); + + std::string calculatePriceResult; + taxi::citymobil::RawApi::CalculatePriceBody calculatePrice(from, to, tariffGroups); + TEST(taxi::citymobil::RawApi::CalculatePrice(calculatePrice, calculatePriceResult, url), ()); + + return taxi::citymobil::MakeProductsFromJson(calculatePriceResult); +} + + taxi::ProvidersContainer GetProvidersSynchronous(taxi::Engine const & engine, ms::LatLon const & from, ms::LatLon const & to, taxi::Delegate & delegate, std::string const & url) @@ -217,6 +250,12 @@ taxi::ProvidersContainer GetProvidersSynchronous(taxi::Engine const & engine, case taxi::Provider::Type::Freenow: providers.emplace_back(taxi::Provider::Type::Freenow, GetFreenowSynchronous(from, to, url)); break; + case taxi::Provider::Type::Yango: + providers.emplace_back(taxi::Provider::Type::Yango, GetYandexSynchronous(from, to, url)); + break; + case taxi::Provider::Type::Citymobil: + providers.emplace_back(taxi::Provider::Type::Citymobil, GetCitymobilSynchronous(from, to, url)); + break; case taxi::Provider::Type::Count: LOG(LERROR, ()); break; @@ -471,7 +510,8 @@ UNIT_CLASS_TEST(AsyncGuiThread, TaxiEngine_Smoke) {taxi::Provider::Type::Yandex, kTesturl}, {taxi::Provider::Type::Maxim, kTesturl}, {taxi::Provider::Type::Rutaxi, kTesturl}, - {taxi::Provider::Type::Freenow, kTesturl}}); + {taxi::Provider::Type::Freenow, kTesturl}, + {taxi::Provider::Type::Citymobil, kTesturl}}); engine.SetDelegate(std::make_unique()); BelarusMinskDelegate delegate; @@ -544,7 +584,7 @@ UNIT_TEST(TaxiEngine_GetProvidersAtPos) engine.SetDelegate(std::make_unique()); providers = engine.GetProvidersAtPos(latlon); TEST_EQUAL(providers.size(), 1, ()); - TEST_EQUAL(providers[0], taxi::Provider::Type::Yandex, ()); + TEST_EQUAL(providers[0], taxi::Provider::Type::Citymobil, ()); engine.SetDelegate(std::make_unique()); providers = engine.GetProvidersAtPos(latlon); @@ -554,5 +594,10 @@ UNIT_TEST(TaxiEngine_GetProvidersAtPos) providers = engine.GetProvidersAtPos(latlon); TEST_EQUAL(providers.size(), 1, ()); TEST_EQUAL(providers[0], taxi::Provider::Type::Freenow, ()); + + engine.SetDelegate(std::make_unique()); + providers = engine.GetProvidersAtPos(latlon); + TEST_EQUAL(providers.size(), 1, ()); + TEST_EQUAL(providers[0], taxi::Provider::Type::Citymobil, ()); } } // namespace diff --git a/partners_api/taxi_engine.cpp b/partners_api/taxi_engine.cpp index 9c9049146c..49ad3987cd 100644 --- a/partners_api/taxi_engine.cpp +++ b/partners_api/taxi_engine.cpp @@ -1,5 +1,6 @@ #include "partners_api/taxi_engine.hpp" +#include "partners_api/citymobil_api.hpp" #include "partners_api/freenow_api.hpp" #include "partners_api/maxim_api.hpp" #include "partners_api/rutaxi_api.hpp" @@ -122,6 +123,7 @@ Engine::Engine(std::vector urls /* = {} */) AddApi(urls, Provider::Type::Yandex); AddApi(urls, Provider::Type::Freenow); AddApi(urls, Provider::Type::Yango); + AddApi(urls, Provider::Type::Citymobil); } void Engine::SetDelegate(std::unique_ptr delegate) diff --git a/partners_api/taxi_places_loader.cpp b/partners_api/taxi_places_loader.cpp index 94da6dc8bf..e66e640676 100644 --- a/partners_api/taxi_places_loader.cpp +++ b/partners_api/taxi_places_loader.cpp @@ -56,6 +56,7 @@ std::string Loader::GetFileNameByProvider(Provider::Type const type) case Provider::Type::Yandex: return "taxi_places/yandex.json"; case Provider::Type::Freenow: return "taxi_places/freenow.json"; case Provider::Type::Yango: return "taxi_places/yango.json"; + case Provider::Type::Citymobil: return "taxi_places/citymobil.json"; case Provider::Type::Count: LOG(LERROR, ("Incorrect taxi provider")); return ""; } UNREACHABLE(); diff --git a/partners_api/taxi_provider.hpp b/partners_api/taxi_provider.hpp index 23f2b2cfca..6d48178213 100644 --- a/partners_api/taxi_provider.hpp +++ b/partners_api/taxi_provider.hpp @@ -19,7 +19,7 @@ struct Product class Provider { public: - enum Type + enum class Type { Uber, Yandex, @@ -27,6 +27,7 @@ public: Rutaxi, Freenow, Yango, + Citymobil, Count }; @@ -86,6 +87,7 @@ inline std::string DebugPrint(Provider::Type type) case Provider::Type::Rutaxi: return "Rutaxi"; case Provider::Type::Freenow: return "Freenow"; case Provider::Type::Yango: return "Yango"; + case Provider::Type::Citymobil: return "Citymobil"; case Provider::Type::Count: ASSERT(false, ()); return ""; } UNREACHABLE(); diff --git a/tools/python/ResponseProvider.py b/tools/python/ResponseProvider.py index 8c74ee6bbd..dd7b8a5cc3 100644 --- a/tools/python/ResponseProvider.py +++ b/tools/python/ResponseProvider.py @@ -153,6 +153,8 @@ class ResponseProvider: "/partners/oauth/token": self.freenow_auth_token, "/partners/service-types": self.freenow_service_types, "/gallery/v2/map": self.guides_on_map_gallery, + "/partners/get_supported_tariffs": self.citymobil_supported_tariffs, + "/partners/calculate_price": self.citymobil_calculate_price, }[url]() except: return self.test_404() @@ -263,6 +265,12 @@ class ResponseProvider: def guides_on_map_gallery(self): return Payload(jsons.GUIDES_ON_MAP_GALLERY) + def citymobil_supported_tariffs(self): + return Payload(jsons.CITYMOBIL_SUPPORTED_TARIFFS) + + def citymobil_calculate_price(self): + return Payload(jsons.CITYMOBIL_CALCULATE_PRICE) + def kill(self): logging.debug("Kill called in ResponseProvider") self.delegate.kill() diff --git a/tools/python/jsons.py b/tools/python/jsons.py index f3b06eedc3..634e8f7baf 100644 --- a/tools/python/jsons.py +++ b/tools/python/jsons.py @@ -726,3 +726,232 @@ GUIDES_ON_MAP_GALLERY = """ } } """ + +CITYMOBIL_SUPPORTED_TARIFFS = """ +{ + "tariff_groups": [ + { + "tariff_group_id": 2, + "tariff_options": [], + "tariff_detail": { + "is_waypoints_enabled": false, + "is_by_instruction_enabled": true, + "is_options_enabled": true, + "order_time_type": [ + "asap", + "delay" + ], + "description": "Таксипортация для ежедневных поездок", + "luxury_level": 1, + "car_models": "Kia Rio, Hyundai Solaris, VW Polo", + "car_image": "https://external-storage.city-mobil.ru/generated/storage_files/Polo.png", + "fastest_car_image": "https://external-storage.city-mobil.ru/generated/storage_files/econom_vw.png" + } + }, + { + "tariff_group_id": 4, + "tariff_options": [], + "tariff_detail": { + "is_waypoints_enabled": false, + "is_by_instruction_enabled": true, + "is_options_enabled": true, + "order_time_type": [ + "asap", + "delay" + ], + "description": "Комфортная таксипортация", + "luxury_level": 3, + "car_models": "Skoda Octavia, Hyundai Elantra, Kia Cerato", + "car_image": "https://external-storage.city-mobil.ru/generated/storage_files/Octavia.png", + "fastest_car_image": "https://external-storage.city-mobil.ru/generated/storage_files/comfort_shkoda.png" + } + }, + { + "tariff_group_id": 13, + "tariff_options": [], + "tariff_detail": { + "is_waypoints_enabled": false, + "is_by_instruction_enabled": true, + "is_options_enabled": true, + "order_time_type": [ + "asap", + "delay" + ], + "description": "Таксипортация с повышенным комфортом", + "luxury_level": 4, + "car_models": "Kia Optima, Toyota Camry, Hyundai Sonata", + "car_image": "https://external-storage.city-mobil.ru/generated/storage_files/Optima.png", + "fastest_car_image": "https://external-storage.city-mobil.ru/generated/storage_files/comfort_plus_kia.png" + } + }, + { + "tariff_group_id": 5, + "tariff_options": [], + "tariff_detail": { + "is_waypoints_enabled": false, + "is_by_instruction_enabled": true, + "is_options_enabled": true, + "order_time_type": [ + "asap", + "delay" + ], + "description": "Для важных встреч", + "luxury_level": 5, + "car_models": "BMW 5, Mercedes E-klasse, Audi A6", + "car_image": "https://external-storage.city-mobil.ru/generated/storage_files/E-Klasse.png", + "fastest_car_image": "https://external-storage.city-mobil.ru/generated/storage_files/buisness_merce.png" + } + }, + { + "tariff_group_id": 3, + "tariff_options": [], + "tariff_detail": { + "is_waypoints_enabled": false, + "is_by_instruction_enabled": true, + "is_options_enabled": true, + "order_time_type": [ + "asap", + "delay" + ], + "description": "", + "luxury_level": 0, + "car_models": "Ford Focus, Kia Rio, Nissan Almera", + "car_image": "", + "fastest_car_image": "" + } + }, + { + "tariff_group_id": 7, + "tariff_options": [], + "tariff_detail": { + "is_waypoints_enabled": false, + "is_by_instruction_enabled": true, + "is_options_enabled": true, + "order_time_type": [ + "asap", + "delay" + ], + "description": "Таксипортация для большой компании", + "luxury_level": -1, + "car_models": "Ford Galaxy, Citroen C4 Picasso, Chevrolet Orlando", + "car_image": "https://external-storage.city-mobil.ru/generated/storage_files/C4.png", + "fastest_car_image": "https://external-storage.city-mobil.ru/generated/storage_files/miniven_citrienC4.png" + } + }, + { + "tariff_group_id": 27, + "tariff_options": [ + { + "id": 99, + "title": "От двери до двери", + "type": "delivery_tariff_door_to_door", + "is_toggle_selection": true, + "description": "Водитель сам заберет посылку у вас и доставит ее адресату до квартиры" + } + ], + "tariff_detail": { + "is_waypoints_enabled": false, + "is_by_instruction_enabled": false, + "is_options_enabled": false, + "order_time_type": [ + "asap" + ], + "description": "", + "luxury_level": -1, + "car_models": "Когда можно не ехать, но нужно передать документы, ключи или чемодан", + "car_image": "https://external-storage.city-mobil.ru/generated/storage_files/Delivery_day.png", + "fastest_car_image": "" + } + } + ] +} +""" + +CITYMOBIL_CALCULATE_PRICE = """ +{ + "distance_text": "8 км", + "duration_text": "15 мин", + "id_calculation": "494d1fc56850ae4411e86fe2d902abe9", + "prices": [ + { + "coefficient": 1, + "fixed_price": true, + "has_discount": false, + "id_tariff": 4098, + "id_tariff_group": 2, + "label": "763₽. В пути ~15 мин", + "new_user_discount": false, + "price": 763, + "total_price": 763, + "tariff_info": { + "name": "Эконом", + "price": "763₽. В пути ~15 мин", + "car_models": "Kia Rio, Hyundai Solaris, VW Polo, Renault Logan, Skoda Rapid, Nissan Almera, Chevrolet Aveo, Ford Focus.", + "car_capacity": "Пассажиров: 4, мест багажа: 2", + "link": "https://t.city-mobil.ru/view/tariffs/2/ru?id_tariff=4098", + "details": [ + { + "text": "Стоимость поездки", + "value": "763₽" + } + ] + } + }, + { + "coefficient": 1, + "fixed_price": true, + "has_discount": false, + "id_tariff": 2, + "id_tariff_group": 4, + "label": "816₽. В пути ~15 мин", + "new_user_discount": false, + "price": 816, + "total_price": 816, + "tariff_info": { + "name": "Комфорт", + "price": "816₽. В пути ~15 мин", + "car_models": "Skoda Octavia, Hyundai Elantra, Kia Cerato, Toyota Camry, Nissan Teana, Ford Mondeo, Kia Ceed, Hyundai Sonata", + "car_capacity": "Пассажиров: 4, мест багажа: 2. Кондиционер обязателен.", + "link": "https://t.city-mobil.ru/view/tariffs/2/ru?id_tariff=2", + "details": [ + { + "text": "Стоимость поездки", + "value": "816₽" + } + ] + } + }, + { + "coefficient": 1, + "fixed_price": true, + "has_discount": false, + "id_tariff": 144, + "id_tariff_group": 5, + "label": "1000₽. В пути ~15 мин", + "new_user_discount": false, + "price": 1000, + "total_price": 1000, + "tariff_info": { + "name": "Бизнес", + "price": "1000₽. В пути ~15 мин", + "car_models": "BMW 5, Mercedes E-klasse, Audi A6, Lexus GS, Hyundai Equus", + "car_capacity": "Пассажиров: 4, мест багажа: 2", + "link": "https://t.city-mobil.ru/view/tariffs/2/ru?id_tariff=144", + "details": [ + { + "text": "Стоимость поездки", + "value": "1000₽" + } + ] + } + } + ], + "route": { + "distance": 7250, + "duration": 846, + "points": "" + }, + "service_status": 1, + "eta": 600 +} +"""