diff --git a/partners_api/CMakeLists.txt b/partners_api/CMakeLists.txt index 751f38831b..6d17934ee3 100644 --- a/partners_api/CMakeLists.txt +++ b/partners_api/CMakeLists.txt @@ -11,6 +11,8 @@ set( banner.hpp booking_api.cpp booking_api.hpp + cian_api.cpp + cian_api.hpp facebook_ads.cpp facebook_ads.hpp mopub_ads.cpp diff --git a/partners_api/cian_api.cpp b/partners_api/cian_api.cpp new file mode 100644 index 0000000000..1ed40af208 --- /dev/null +++ b/partners_api/cian_api.cpp @@ -0,0 +1,133 @@ +#include "partners_api/cian_api.hpp" + +#include "platform/http_client.hpp" +#include "platform/platform.hpp" + +#include "geometry/rect2d.hpp" + +#include "base/logging.hpp" + +#include + +#include "3party/jansson/myjansson.hpp" + +using namespace platform; + +namespace +{ +bool RunSimpleHttpRequest(string const & url, string & result) +{ + HttpClient request(url); + if (request.RunHttpRequest() && !request.WasRedirected() && request.ErrorCode() == 200) + { + result = request.ServerResponse(); + return true; + } + return false; +} + +void MakeResult(std::string const & src, std::vector & result) +{ + my::Json root(src.c_str()); + if (!json_is_object(root.get())) + MYTHROW(my::Json::Exception, ("The answer must contain a json object.")); + + json_t * clusters = json_object_get(root.get(), "clusters"); + + if (clusters == nullptr) + return; + + size_t const clustersSize = json_array_size(clusters); + + if (clustersSize == 0) + return; + + for (size_t i = 0; i < clustersSize; ++i) + { + auto cluster = json_array_get(clusters, i); + cian::RentPlace place; + FromJSONObject(cluster, "lat", place.m_latlon.lat); + FromJSONObject(cluster, "lng", place.m_latlon.lon); + + json_t * offers = json_object_get(cluster, "offers"); + + if (offers == nullptr) + return; + + size_t const offersSize = json_array_size(offers); + + if (offersSize == 0) + return; + + for (size_t i = 0; i < offersSize; ++i) + { + auto offer = json_array_get(offers, i); + + cian::RentOffer rentOffer; + + FromJSONObject(offer, "flatType", rentOffer.m_flatType); + FromJSONObject(offer, "roomsCount", rentOffer.m_roomsCount); + FromJSONObject(offer, "priceRur", rentOffer.m_priceRur); + FromJSONObject(offer, "floorNumber", rentOffer.m_floorNumber); + FromJSONObject(offer, "floorsCount", rentOffer.m_floorsCount); + FromJSONObject(offer, "url", rentOffer.m_url); + FromJSONObject(offer, "address", rentOffer.m_address); + + place.m_offers.push_back(std::move(rentOffer)); + } + + result.push_back(std::move(place)); + } +} +} // namespace + +namespace cian +{ +// static +bool RawApi::GetRentNearby(m2::RectD const & rect, std::string & result, + std::string const & baseUrl /* = kBaseUrl */) +{ + ostringstream url; + url << baseUrl << "/get-offers-in-bbox/?bbox=" << rect.minX() << ',' << rect.maxY() << '~' + << rect.maxX() << ',' << rect.minY(); + + return RunSimpleHttpRequest(url.str(), result); +} + +Api::Api(std::string const & baseUrl /* = kBaseUrl */) : m_baseUrl(baseUrl) {} + +Api::~Api() +{ + m_worker.Shutdown(base::WorkerThread::Exit::SkipPending); +} + +uint64_t Api::GetRentNearby(m2::RectD const & rect, RentNearbyCallback const & cb) +{ + auto const reqId = ++m_requestId; + auto const baseUrl = m_baseUrl; + + m_worker.Push([reqId, rect, cb, baseUrl]() { + std::string rawResult; + std::vector result; + + if (!RawApi::GetRentNearby(rect, rawResult, baseUrl)) + { + GetPlatform().RunOnGuiThread([cb, result, reqId]() { cb(result, reqId); }); + return; + } + + try + { + MakeResult(rawResult, result); + } + catch (my::Json::Exception const & e) + { + LOG(LERROR, (e.Msg())); + result.clear(); + } + GetPlatform().RunOnGuiThread([cb, result, reqId]() { cb(result, reqId); }); + }); + + return reqId; +} +} // namespace cian diff --git a/partners_api/cian_api.hpp b/partners_api/cian_api.hpp new file mode 100644 index 0000000000..70b7c2feef --- /dev/null +++ b/partners_api/cian_api.hpp @@ -0,0 +1,57 @@ +#pragma once + +#include "geometry/latlon.hpp" +#include "geometry/rect2d.hpp" + +#include "base/worker_thread.hpp" + +#include +#include +#include +#include + +namespace cian +{ +std::string const kBaseUrl = "https://api.cian.ru/rent-nearby/v1"; + +class RawApi +{ +public: + static bool GetRentNearby(m2::RectD const & rect, std::string & result, + std::string const & url = kBaseUrl); +}; + +struct RentOffer +{ + std::string m_flatType; + uint8_t m_roomsCount = 0; + uint32_t m_priceRur = 0; + uint8_t m_floorNumber = 0; + uint8_t m_floorsCount = 0; + std::string m_url; + std::string m_address; +}; + +struct RentPlace +{ + ms::LatLon m_latlon; + std::vector m_offers; +}; + +using RentNearbyCallback = + std::function const & places, uint64_t const requestId)>; + +class Api +{ +public: + explicit Api(std::string const & baseUrl = kBaseUrl); + virtual ~Api(); + + uint64_t GetRentNearby(m2::RectD const & rect, RentNearbyCallback const & cb); + +private: + uint64_t m_requestId = 0; + std::string m_baseUrl; + base::WorkerThread m_worker; +}; +} // namespace cian diff --git a/partners_api/partners_api.pro b/partners_api/partners_api.pro index bda026f3b6..68c648e443 100644 --- a/partners_api/partners_api.pro +++ b/partners_api/partners_api.pro @@ -12,6 +12,7 @@ SOURCES += \ ads_base.cpp \ ads_engine.cpp \ booking_api.cpp \ + cian_api.cpp \ facebook_ads.cpp \ mopub_ads.cpp \ opentable_api.cpp \ @@ -28,6 +29,7 @@ HEADERS += \ ads_engine.hpp \ banner.hpp \ booking_api.hpp \ + cian_api.hpp \ facebook_ads.hpp \ mopub_ads.hpp \ opentable_api.hpp \ diff --git a/partners_api/partners_api_tests/CMakeLists.txt b/partners_api/partners_api_tests/CMakeLists.txt index 69db749c6f..e9e27c71ac 100644 --- a/partners_api/partners_api_tests/CMakeLists.txt +++ b/partners_api/partners_api_tests/CMakeLists.txt @@ -6,6 +6,7 @@ set( SRC ads_engine_tests.cpp booking_tests.cpp + cian_tests.cpp facebook_tests.cpp mopub_tests.cpp rb_tests.cpp diff --git a/partners_api/partners_api_tests/cian_tests.cpp b/partners_api/partners_api_tests/cian_tests.cpp new file mode 100644 index 0000000000..0f400559d5 --- /dev/null +++ b/partners_api/partners_api_tests/cian_tests.cpp @@ -0,0 +1,38 @@ +#include "testing/testing.hpp" + +#include "partners_api/cian_api.hpp" + +#include "3party/jansson/myjansson.hpp" + +namespace +{ +UNIT_TEST(Cian_GetRentNearbyRaw) +{ + string result; + cian::RawApi::GetRentNearby({37.402891, 55.656318, 37.840971, 55.859980}, result); + + TEST(!result.empty(), ()); + + my::Json root(result.c_str()); + TEST(json_is_object(root.get()), ()); +} + +UNIT_TEST(Cian_GetRentNearby) +{ + cian::Api api("http://localhost:34568/partners"); + m2::RectD rect(37.501365, 55.805666, 37.509873, 55.809183); + + std::vector result; + uint64_t reqId = 0; + reqId = api.GetRentNearby(rect, [&reqId, &result](std::vector const & places, + uint64_t const requestId) { + TEST_EQUAL(reqId, requestId, ()); + result = places; + testing::StopEventLoop(); + }); + + testing::RunEventLoop(); + + TEST(!result.empty(), ()); +} +} // namespace diff --git a/partners_api/partners_api_tests/partners_api_tests.pro b/partners_api/partners_api_tests/partners_api_tests.pro index afdf28f926..279bfc1bbd 100644 --- a/partners_api/partners_api_tests/partners_api_tests.pro +++ b/partners_api/partners_api_tests/partners_api_tests.pro @@ -28,6 +28,7 @@ SOURCES += \ $$ROOT_DIR/testing/testingmain.cpp \ ads_engine_tests.cpp \ booking_tests.cpp \ + cian_tests.cpp \ facebook_tests.cpp \ mopub_tests.cpp \ rb_tests.cpp \ diff --git a/tools/python/ResponseProvider.py b/tools/python/ResponseProvider.py index 881a444861..56f2e0e9b9 100644 --- a/tools/python/ResponseProvider.py +++ b/tools/python/ResponseProvider.py @@ -144,6 +144,7 @@ class ResponseProvider: "/booking/min_price": self.partners_minprice, "/booking/min_price.getHotelAvailability": self.partners_minprice, "/partners/taxi_info": self.partners_yandex_taxi_info, + "/partners/get-offers-in-bbox/": self.partners_rent_nearby, }[url]() except: return self.test_404() @@ -225,6 +226,9 @@ class ResponseProvider: def partners_yandex_taxi_info(self): return Payload(jsons.PARTNERS_TAXI_INFO) + def partners_rent_nearby(self): + return Payload(jsons.PARTNERS_RENT_NEARBY) + 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 b495d507fb..d20b45f0ba 100644 --- a/tools/python/jsons.py +++ b/tools/python/jsons.py @@ -1,3 +1,5 @@ +# coding=utf-8 + PARTNERS_PRICE = """ { "prices": [ @@ -248,3 +250,168 @@ PARTNERS_TAXI_INFO = """ "time": 1057.7440430297368 } """ + +PARTNERS_RENT_NEARBY = """ +{ + "clusters": [ + { + "lat": 55.80529, + "lng": 37.508274, + "offers": [ + { + "flatType": "rooms", + "roomsCount": 2, + "priceRur": 50000, + "floorNumber": 8, + "floorsCount": 9, + "photosCount": 11, + "url": "https://cian.ru/rent/flat/159026341", + "address": "Ленинградский просп., 77К2" + }, + { + "flatType": "rooms", + "roomsCount": 1, + "priceRur": 39999, + "floorNumber": 1, + "floorsCount": 12, + "photosCount": 13, + "url": "https://cian.ru/rent/flat/157827964", + "address": "Ленинградский просп., 77К2" + }, + { + "flatType": "rooms", + "roomsCount": 2, + "priceRur": 58000, + "floorNumber": 6, + "floorsCount": 9, + "photosCount": 9, + "url": "https://cian.ru/rent/flat/159523671", + "address": "Ленинградский просп., 77К2" + } + ], + "count": 3, + "url": "https://cian.ru/cat.php?deal_type=rent&offer_type=flat&engine_version=2&house%5B0%5D=26696" + }, + { + "lat": 55.805776, + "lng": 37.50946, + "offers": [ + { + "flatType": "rooms", + "roomsCount": 1, + "priceRur": 39000, + "floorNumber": 2, + "floorsCount": 9, + "photosCount": 9, + "url": "https://cian.ru/rent/flat/158786442", + "address": "Ленинградский просп., 77А" + } + ], + "count": 1, + "url": "https://cian.ru/cat.php?deal_type=rent&offer_type=flat&engine_version=2&house%5B0%5D=2009028" + }, + { + "lat": 55.808306, + "lng": 37.5008, + "offers": [ + { + "flatType": "rooms", + "roomsCount": 2, + "priceRur": 50000, + "floorNumber": 6, + "floorsCount": 10, + "photosCount": 9, + "url": "https://cian.ru/rent/flat/155419837", + "address": "Волоколамское ш., 6" + } + ], + "count": 1, + "url": "https://cian.ru/cat.php?deal_type=rent&offer_type=flat&engine_version=2&house%5B0%5D=1692086" + }, + { + "lat": 55.805999, + "lng": 37.503738, + "offers": [ + { + "flatType": "rooms", + "roomsCount": 2, + "priceRur": 70000, + "floorNumber": 4, + "floorsCount": 9, + "photosCount": 0, + "url": "https://cian.ru/rent/flat/159765700", + "address": "Волоколамское ш., 1кА" + } + ], + "count": 1, + "url": "https://cian.ru/cat.php?deal_type=rent&offer_type=flat&engine_version=2&house%5B0%5D=1009214" + }, + { + "lat": 55.805361, + "lng": 37.507124, + "offers": [ + { + "flatType": "rooms", + "roomsCount": 1, + "priceRur": 45000, + "floorNumber": 1, + "floorsCount": 9, + "photosCount": 6, + "url": "https://cian.ru/rent/flat/158673214", + "address": "Ленинградский просп., 77К3" + }, + { + "flatType": "rooms", + "roomsCount": 2, + "priceRur": 48000, + "floorNumber": 5, + "floorsCount": 9, + "photosCount": 15, + "url": "https://cian.ru/rent/flat/158613232", + "address": "Ленинградский просп., 77К3" + }, + { + "flatType": "rooms", + "roomsCount": 1, + "priceRur": 45000, + "floorNumber": 1, + "floorsCount": 9, + "photosCount": 7, + "url": "https://cian.ru/rent/flat/159035369", + "address": "Ленинградский просп., 77К3" + } + ], + "count": 3, + "url": "https://cian.ru/cat.php?deal_type=rent&offer_type=flat&engine_version=2&house%5B0%5D=26698" + }, + { + "lat": 55.809226, + "lng": 37.504978, + "offers": [ + { + "flatType": "rooms", + "roomsCount": 2, + "priceRur": 60000, + "floorNumber": 5, + "floorsCount": 8, + "photosCount": 37, + "url": "https://cian.ru/rent/flat/157212858", + "address": "Ленинградское ш., 3С1" + }, + { + "flatType": "rooms", + "roomsCount": 2, + "priceRur": 35000, + "floorNumber": 6, + "floorsCount": 8, + "photosCount": 5, + "url": "https://cian.ru/rent/flat/158689565", + "address": "Ленинградское ш., 3С1" + } + ], + "count": 2, + "url": "https://cian.ru/cat.php?deal_type=rent&offer_type=flat&engine_version=2&house%5B0%5D=583624" + } + ] +} +""" diff --git a/xcode/partners_api/partners_api.xcodeproj/project.pbxproj b/xcode/partners_api/partners_api.xcodeproj/project.pbxproj index bc604d56c7..89218e3ccc 100644 --- a/xcode/partners_api/partners_api.xcodeproj/project.pbxproj +++ b/xcode/partners_api/partners_api.xcodeproj/project.pbxproj @@ -24,6 +24,9 @@ 3D47B29B1F054C89000828D2 /* taxi_countries.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3D47B2971F054C89000828D2 /* taxi_countries.cpp */; }; 3D47B29C1F054C89000828D2 /* taxi_countries.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 3D47B2981F054C89000828D2 /* taxi_countries.hpp */; }; 3D47B29D1F054C89000828D2 /* taxi_places.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 3D47B2991F054C89000828D2 /* taxi_places.hpp */; }; + 3D47B2AC1F14BE89000828D2 /* cian_api.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3D47B2AA1F14BE89000828D2 /* cian_api.cpp */; }; + 3D47B2AD1F14BE89000828D2 /* cian_api.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 3D47B2AB1F14BE89000828D2 /* cian_api.hpp */; }; + 3D47B2AF1F14BE94000828D2 /* cian_tests.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3D47B2AE1F14BE94000828D2 /* cian_tests.cpp */; }; 3DBC1C541E4B14920016897F /* facebook_ads.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3DBC1C521E4B14920016897F /* facebook_ads.cpp */; }; 3DBC1C551E4B14920016897F /* facebook_ads.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 3DBC1C531E4B14920016897F /* facebook_ads.hpp */; }; 3DFEBF851EF82BEA00317D5C /* viator_api.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3DFEBF831EF82BEA00317D5C /* viator_api.cpp */; }; @@ -75,6 +78,9 @@ 3D47B2971F054C89000828D2 /* taxi_countries.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = taxi_countries.cpp; sourceTree = ""; }; 3D47B2981F054C89000828D2 /* taxi_countries.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = taxi_countries.hpp; sourceTree = ""; }; 3D47B2991F054C89000828D2 /* taxi_places.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = taxi_places.hpp; sourceTree = ""; }; + 3D47B2AA1F14BE89000828D2 /* cian_api.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = cian_api.cpp; sourceTree = ""; }; + 3D47B2AB1F14BE89000828D2 /* cian_api.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = cian_api.hpp; sourceTree = ""; }; + 3D47B2AE1F14BE94000828D2 /* cian_tests.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = cian_tests.cpp; sourceTree = ""; }; 3DBC1C501E4B14810016897F /* facebook_tests.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = facebook_tests.cpp; sourceTree = ""; }; 3DBC1C521E4B14920016897F /* facebook_ads.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = facebook_ads.cpp; sourceTree = ""; }; 3DBC1C531E4B14920016897F /* facebook_ads.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = facebook_ads.hpp; sourceTree = ""; }; @@ -160,6 +166,8 @@ F6B5363B1DA520B20067EEA5 /* partners_api */ = { isa = PBXGroup; children = ( + 3D47B2AA1F14BE89000828D2 /* cian_api.cpp */, + 3D47B2AB1F14BE89000828D2 /* cian_api.hpp */, 3D47B2961F054C89000828D2 /* taxi_base.cpp */, 3D47B2971F054C89000828D2 /* taxi_countries.cpp */, 3D47B2981F054C89000828D2 /* taxi_countries.hpp */, @@ -197,6 +205,7 @@ F6B536441DA521060067EEA5 /* partners_api_tests */ = { isa = PBXGroup; children = ( + 3D47B2AE1F14BE94000828D2 /* cian_tests.cpp */, 3D47B2801F00F94D000828D2 /* mopub_tests.cpp */, 3DFEBF871EF82C1300317D5C /* viator_tests.cpp */, 3DFEBFA01EFBFC2300317D5C /* taxi_engine_tests.cpp */, @@ -250,6 +259,7 @@ 3DFEBF861EF82BEA00317D5C /* viator_api.hpp in Headers */, 346E889C1E9D087400D4CE9B /* rb_ads.hpp in Headers */, 3DFEBF9A1EFBFC1500317D5C /* taxi_base.hpp in Headers */, + 3D47B2AD1F14BE89000828D2 /* cian_api.hpp in Headers */, 346E889A1E9D087400D4CE9B /* banner.hpp in Headers */, 3DFEBF9F1EFBFC1500317D5C /* yandex_api.hpp in Headers */, 3DFEBF9D1EFBFC1500317D5C /* taxi_provider.hpp in Headers */, @@ -360,6 +370,7 @@ 3DFEBF9B1EFBFC1500317D5C /* taxi_engine.cpp in Sources */, 346E889B1E9D087400D4CE9B /* rb_ads.cpp in Sources */, 3DBC1C541E4B14920016897F /* facebook_ads.cpp in Sources */, + 3D47B2AF1F14BE94000828D2 /* cian_tests.cpp in Sources */, 346E88981E9D087400D4CE9B /* ads_engine.cpp in Sources */, 3D47B29B1F054C89000828D2 /* taxi_countries.cpp in Sources */, F67E75251DB8F06F00D6741F /* opentable_api.cpp in Sources */, @@ -367,6 +378,7 @@ F6B536401DA520E40067EEA5 /* booking_api.cpp in Sources */, 3D47B29A1F054C89000828D2 /* taxi_base.cpp in Sources */, 3DFEBF9E1EFBFC1500317D5C /* yandex_api.cpp in Sources */, + 3D47B2AC1F14BE89000828D2 /* cian_api.cpp in Sources */, ); runOnlyForDeploymentPostprocessing = 0; };