[eye] events registration + trimming + tests

This commit is contained in:
Arsentiy Milchakov 2018-10-30 16:08:22 +03:00 committed by Tatiana Yan
parent 9489e15c9b
commit cf0be3505a
7 changed files with 360 additions and 63 deletions

View file

@ -10,6 +10,7 @@
#include "base/logging.hpp"
#include <algorithm>
#include <chrono>
#include <memory>
#include <utility>
#include <vector>
@ -18,6 +19,9 @@ using namespace eye;
namespace
{
// Three months.
auto constexpr kMapObjectEventsExpirePeriod = std::chrono::hours(24 * 30 * 3);
void Load(Info & info)
{
Storage::Migrate();
@ -49,22 +53,20 @@ bool Save(Info const & info)
return Storage::SaveInfo(fileData);
}
// TODO: use to trim old map object events.
// bool SaveMapObjects(Info const & info)
//{
// std::vector<int8_t> fileData;
// Serdes::SerializeMapObjects(info.m_mapObjects, fileData);
// return Storage::SaveMapObjects(fileData);
//}
//
// TODO: use it to save map object events with append only flag.
// bool SaveMapObjectEvent(MapObject const & mapObject, MapObject::Event const & event)
//{
// std::vector<int8_t> eventData;
// Serdes::SerializeMapObjectEvent(mapObject, event, eventData);
//
// return Storage::AppendMapObjectEvent(eventData);
//}
bool SaveMapObjects(MapObjects const & mapObjects)
{
std::vector<int8_t> fileData;
Serdes::SerializeMapObjects(mapObjects, fileData);
return Storage::SaveMapObjects(fileData);
}
bool SaveMapObjectEvent(MapObject const & mapObject, MapObject::Event const & event)
{
std::vector<int8_t> eventData;
Serdes::SerializeMapObjectEvent(mapObject, event, eventData);
return Storage::AppendMapObjectEvent(eventData);
}
} // namespace
namespace eye
@ -74,6 +76,11 @@ Eye::Eye()
Info info;
Load(info);
m_info.Set(std::make_shared<Info>(info));
GetPlatform().RunTask(Platform::Thread::File, [this]
{
TrimExpiredMapObjectEvents();
});
}
// static
@ -107,6 +114,37 @@ bool Eye::Save(InfoType const & info)
return true;
}
void Eye::TrimExpiredMapObjectEvents()
{
auto const info = m_info.Get();
auto editableInfo = std::make_shared<Info>(*info);
auto changed = false;
for (auto it = editableInfo->m_mapObjects.begin(); it != editableInfo->m_mapObjects.end();)
{
auto & events = it->second;
events.erase(std::remove_if(events.begin(), events.end(), [&changed](auto const & item)
{
if (Clock::now() - item.m_eventTime >= kMapObjectEventsExpirePeriod)
{
if (!changed)
changed = true;
return true;
}
return false;
}), events.end());
if (events.empty())
it = editableInfo->m_mapObjects.erase(it);
else
++it;
}
if (changed && SaveMapObjects(editableInfo->m_mapObjects))
m_info.Set(editableInfo);
}
void Eye::RegisterTipClick(Tip::Type type, Tip::Event event)
{
auto const info = m_info.Get();
@ -265,29 +303,42 @@ void Eye::RegisterLayerShown(Layer::Type type)
});
}
void Eye::RegisterPlacePageOpened()
void Eye::RegisterMapObjectEvent(MapObject const & mapObject, MapObject::Event::Type type,
ms::LatLon const & userPos)
{
auto const info = m_info.Get();
auto editableInfo = std::make_shared<Info>(*info);
auto & mapObjects = editableInfo->m_mapObjects;
}
MapObject::Event event;
event.m_type = type;
event.m_userPos = userPos;
event.m_eventTime = Clock::now();
void Eye::RegisterUgcEditorOpened()
{
MapObject::Events events;
auto it = mapObjects.find(mapObject);
if (it == mapObjects.end())
{
events = {event};
mapObjects.emplace(mapObject, std::move(events));
}
else
{
it->second.push_back(event);
events = it->second;
}
}
void Eye::RegisterUgcSaved()
{
}
void Eye::RegisterAddToBookmarkClicked()
{
}
void Eye::RegisterRouteCreatedToObject()
{
if (!SaveMapObjectEvent(mapObject, event))
return;
m_info.Set(editableInfo);
GetPlatform().RunTask(Platform::Thread::Gui, [this, mapObject, events]
{
for (auto subscriber : m_subscribers)
{
subscriber->OnMapObjectEvent(mapObject, events);
}
});
}
// Eye::Event methods ------------------------------------------------------------------------------
@ -346,47 +397,56 @@ void Eye::Event::LayerShown(Layer::Type type)
}
// static
void Eye::Event::PlacePageOpened()
void Eye::Event::PlacePageOpened(std::string const & bestType, ms::LatLon const & latLon,
ms::LatLon const & userPos)
{
GetPlatform().RunTask(Platform::Thread::File, []
GetPlatform().RunTask(Platform::Thread::File, [bestType, latLon, userPos]
{
Instance().RegisterPlacePageOpened();
Instance().RegisterMapObjectEvent({bestType, latLon}, MapObject::Event::Type::Open, userPos);
});
}
// static
void Eye::Event::UgcEditorOpened()
void Eye::Event::UgcEditorOpened(std::string const & bestType, ms::LatLon const & latLon,
ms::LatLon const & userPos)
{
GetPlatform().RunTask(Platform::Thread::File, []
GetPlatform().RunTask(Platform::Thread::File, [bestType, latLon, userPos]
{
Instance().RegisterUgcEditorOpened();
Instance().RegisterMapObjectEvent({bestType, latLon}, MapObject::Event::Type::UgcEditorOpened,
userPos);
});
}
// static
void Eye::Event::UgcSaved()
void Eye::Event::UgcSaved(std::string const & bestType, ms::LatLon const & latLon,
ms::LatLon const & userPos)
{
GetPlatform().RunTask(Platform::Thread::File, []
GetPlatform().RunTask(Platform::Thread::File, [bestType, latLon, userPos]
{
Instance().RegisterUgcSaved();
Instance().RegisterMapObjectEvent({bestType, latLon}, MapObject::Event::Type::UgcSaved,
userPos);
});
}
// static
void Eye::Event::AddToBookmarkClicked()
void Eye::Event::AddToBookmarkClicked(std::string const & bestType, ms::LatLon const & latLon,
ms::LatLon const & userPos)
{
GetPlatform().RunTask(Platform::Thread::File, []
GetPlatform().RunTask(Platform::Thread::File, [bestType, latLon, userPos]
{
Instance().RegisterAddToBookmarkClicked();
Instance().RegisterMapObjectEvent({bestType, latLon}, MapObject::Event::Type::AddToBookmark,
userPos);
});
}
// static
void Eye::Event::RouteCreatedToObject()
void Eye::Event::RouteCreatedToObject(std::string const & bestType, ms::LatLon const & latLon,
ms::LatLon const & userPos)
{
GetPlatform().RunTask(Platform::Thread::File, []
GetPlatform().RunTask(Platform::Thread::File, [bestType, latLon, userPos]
{
Instance().RegisterRouteCreatedToObject();
Instance().RegisterMapObjectEvent({bestType, latLon}, MapObject::Event::Type::RouteToCreated,
userPos);
});
}
} // namespace eye

View file

@ -5,6 +5,7 @@
#include "base/atomic_shared_ptr.hpp"
#include "base/macros.hpp"
#include <string>
#include <vector>
namespace eye
@ -21,7 +22,7 @@ public:
virtual void OnDiscoveryShown(Time const & time) {}
virtual void OnDiscoveryItemClicked(Discovery::Event event) {}
virtual void OnLayerShown(Layer const & layer) {}
virtual void OnPlacePageOpened(MapObject const & poi) {}
virtual void OnMapObjectEvent(MapObject const & poi, MapObject::Events const & events) {}
};
// Note This class IS thread-safe.
@ -43,11 +44,16 @@ public:
static void DiscoveryShown();
static void DiscoveryItemClicked(Discovery::Event event);
static void LayerShown(Layer::Type type);
static void PlacePageOpened();
static void UgcEditorOpened();
static void UgcSaved();
static void AddToBookmarkClicked();
static void RouteCreatedToObject();
static void PlacePageOpened(std::string const & bestType, ms::LatLon const & latLon,
ms::LatLon const & userPos);
static void UgcEditorOpened(std::string const & bestType, ms::LatLon const & latLon,
ms::LatLon const & userPos);
static void UgcSaved(std::string const & bestType, ms::LatLon const & latLon,
ms::LatLon const & userPos);
static void AddToBookmarkClicked(std::string const & bestType, ms::LatLon const & latLon,
ms::LatLon const & userPos);
static void RouteCreatedToObject(std::string const & bestType, ms::LatLon const & latLon,
ms::LatLon const & userPos);
};
static Eye & Instance();
@ -62,6 +68,7 @@ private:
Eye();
bool Save(InfoType const & info);
void TrimExpiredMapObjectEvents();
// Event processing:
void RegisterTipClick(Tip::Type type, Tip::Event event);
@ -70,11 +77,8 @@ private:
void UpdateDiscoveryShownTime();
void IncrementDiscoveryItem(Discovery::Event event);
void RegisterLayerShown(Layer::Type type);
void RegisterPlacePageOpened();
void RegisterUgcEditorOpened();
void RegisterUgcSaved();
void RegisterAddToBookmarkClicked();
void RegisterRouteCreatedToObject();
void RegisterMapObjectEvent(MapObject const & mapObject, MapObject::Event::Type type,
ms::LatLon const & userPos);
base::AtomicSharedPtr<Info> m_info;
std::vector<Subscriber *> m_subscribers;

View file

@ -10,6 +10,7 @@
#include <array>
#include <chrono>
#include <cstdint>
#include <deque>
#include <string>
#include <type_traits>
#include <unordered_map>
@ -181,6 +182,8 @@ struct MapObject
Time m_eventTime;
};
using Events = std::deque<Event>;
struct Hash
{
size_t operator()(MapObject const & p) const
@ -199,7 +202,7 @@ struct MapObject
ms::LatLon m_pos;
};
using MapObjects = std::unordered_map<MapObject, std::vector<MapObject::Event>, MapObject::Hash>;
using MapObjects = std::unordered_map<MapObject, MapObject::Events, MapObject::Hash>;
struct InfoV0
{

View file

@ -123,7 +123,7 @@ void Serdes::DeserializeMapObjects(std::vector<int8_t> const & bytes, MapObjects
auto it = result.find(poi);
if (it == result.end())
{
std::vector<MapObject::Event> events = {event.m_event};
MapObject::Events events = {event.m_event};
result.emplace(poi, std::move(events));
}
else

View file

@ -7,6 +7,7 @@
#include "metrics/metrics_tests_support/eye_for_testing.hpp"
#include <chrono>
#include <cstdint>
#include <utility>
#include <vector>
@ -43,7 +44,7 @@ Info MakeDefaultInfoForTesting()
poi.m_bestType = "shop";
poi.m_pos = {53.652007, 108.143443};
MapObject::Event eventInfo;
std::vector<MapObject::Event> events;
MapObject::Events events;
eventInfo.m_eventTime = Time(std::chrono::hours(90000));
eventInfo.m_userPos = {72.045507, 81.408095};
eventInfo.m_type = MapObject::Event::Type::AddToBookmark;
@ -385,3 +386,216 @@ UNIT_CLASS_TEST(ScopedEyeForTesting, AppendLayerTest)
TEST_NOT_EQUAL(prevShowTime, lastShownLayerTime, ());
}
}
UNIT_CLASS_TEST(ScopedEyeForTesting, TrimExpiredMapObjectEvents)
{
Info info;
{
MapObject poi;
poi.m_bestType = "shop";
poi.m_pos = {53.652007, 108.143443};
MapObject::Event eventInfo;
MapObject::Events events;
eventInfo.m_eventTime = Clock::now() - std::chrono::hours((24 * 30 * 3) + 1);
eventInfo.m_userPos = {72.045507, 81.408095};
eventInfo.m_type = MapObject::Event::Type::Open;
events.emplace_back(eventInfo);
eventInfo.m_eventTime =
Clock::now() - (std::chrono::hours(24 * 30 * 3) + std::chrono::seconds(1));
eventInfo.m_userPos = {72.045400, 81.408200};
eventInfo.m_type = MapObject::Event::Type::AddToBookmark;
events.emplace_back(eventInfo);
eventInfo.m_eventTime = Clock::now() - std::chrono::hours(24 * 30 * 3);
eventInfo.m_userPos = {72.045450, 81.408201};
eventInfo.m_type = MapObject::Event::Type::RouteToCreated;
events.emplace_back(eventInfo);
info.m_mapObjects.emplace(poi, events);
}
{
MapObject poi;
poi.m_bestType = "cafe";
poi.m_pos = {53.652005, 108.143448};
MapObject::Event eventInfo;
MapObject::Events events;
eventInfo.m_eventTime = Clock::now() - std::chrono::hours(24 * 30 * 3);
eventInfo.m_userPos = {53.016347, 158.683327};
eventInfo.m_type = MapObject::Event::Type::Open;
events.emplace_back(eventInfo);
eventInfo.m_eventTime = Clock::now() - std::chrono::seconds(30);
eventInfo.m_userPos = {53.016347, 158.683327};
eventInfo.m_type = MapObject::Event::Type::UgcEditorOpened;
events.emplace_back(eventInfo);
eventInfo.m_eventTime = Clock::now();
eventInfo.m_userPos = {53.116347, 158.783327};
eventInfo.m_type = MapObject::Event::Type::UgcSaved;
events.emplace_back(eventInfo);
info.m_mapObjects.emplace(poi, events);
}
EyeForTesting::SetInfo(info);
{
auto const resultInfo = Eye::Instance().GetInfo();
auto const & mapObjects = resultInfo->m_mapObjects;
TEST_EQUAL(mapObjects.size(), 2, ());
{
MapObject poi;
poi.m_bestType = "shop";
poi.m_pos = {53.652007, 108.143443};
auto const it = mapObjects.find(poi);
TEST(it != mapObjects.end(), ());
TEST_EQUAL(it->second.size(), 3, ());
TEST_EQUAL(it->second[0].m_type, MapObject::Event::Type::Open, ());
TEST_EQUAL(it->second[1].m_userPos, ms::LatLon(72.045400, 81.408200), ());
TEST_EQUAL(it->second[2].m_userPos, ms::LatLon(72.045450, 81.408201), ());
}
{
MapObject poi;
poi.m_bestType = "cafe";
poi.m_pos = {53.652005, 108.143448};
auto const it = mapObjects.find(poi);
TEST(it != mapObjects.end(), ());
TEST_EQUAL(it->second.size(), 3, ());
TEST_EQUAL(it->second[0].m_type, MapObject::Event::Type::Open, ());
TEST_EQUAL(it->second[1].m_userPos, ms::LatLon(53.016347, 158.683327), ());
TEST_EQUAL(it->second[2].m_userPos, ms::LatLon(53.116347, 158.783327), ());
}
}
EyeForTesting::TrimExpiredMapObjectEvents();
{
auto const resultInfo = Eye::Instance().GetInfo();
auto const & mapObjects = resultInfo->m_mapObjects;
TEST_EQUAL(mapObjects.size(), 1, ());
{
MapObject poi;
poi.m_bestType = "shop";
poi.m_pos = {53.652007, 108.143443};
auto const it = mapObjects.find(poi);
TEST(it == mapObjects.end(), ());
}
{
MapObject poi;
poi.m_bestType = "cafe";
poi.m_pos = {53.652005, 108.143448};
auto const it = mapObjects.find(poi);
TEST(it != mapObjects.end(), ());
TEST_EQUAL(it->second.size(), 2, ());
TEST_EQUAL(it->second[0].m_userPos, ms::LatLon(53.016347, 158.683327), ());
TEST_EQUAL(it->second[0].m_type, MapObject::Event::Type::UgcEditorOpened, ());
TEST_EQUAL(it->second[1].m_userPos, ms::LatLon(53.116347, 158.783327), ());
TEST_EQUAL(it->second[1].m_type, MapObject::Event::Type::UgcSaved, ());
}
}
}
UNIT_CLASS_TEST(ScopedEyeForTesting, RegisterMapObjectEvent)
{
{
MapObject poi;
poi.m_bestType = "cafe";
poi.m_pos = {53.652005, 108.143448};
ms::LatLon userPos = {53.016347, 158.683327};
EyeForTesting::RegisterMapObjectEvent(poi, MapObject::Event::Type::Open, userPos);
userPos = {53.016345, 158.683329};
EyeForTesting::RegisterMapObjectEvent(poi, MapObject::Event::Type::RouteToCreated, userPos);
}
{
MapObject poi;
poi.m_bestType = "shop";
poi.m_pos = {53.652005, 108.143448};
ms::LatLon userPos = {0.0, 0.0};
EyeForTesting::RegisterMapObjectEvent(poi, MapObject::Event::Type::RouteToCreated, userPos);
userPos = {158.016345, 53.683329};
EyeForTesting::RegisterMapObjectEvent(poi, MapObject::Event::Type::AddToBookmark, userPos);
}
{
MapObject poi;
poi.m_bestType = "amenity-bench";
poi.m_pos = {53.652005, 108.143448};
ms::LatLon userPos = {0.0, 0.0};
EyeForTesting::RegisterMapObjectEvent(poi, MapObject::Event::Type::Open, userPos);
}
{
auto const resultInfo = Eye::Instance().GetInfo();
auto const & mapObjects = resultInfo->m_mapObjects;
TEST_EQUAL(mapObjects.size(), 3, ());
{
MapObject poi;
poi.m_bestType = "cafe";
poi.m_pos = {53.652005, 108.143448};
auto const it = mapObjects.find(poi);
TEST(it != mapObjects.end(), ());
TEST_EQUAL(it->second.size(), 2, ());
TEST_EQUAL(it->second[0].m_userPos, ms::LatLon(53.016347, 158.683327), ());
TEST_EQUAL(it->second[0].m_type, MapObject::Event::Type::Open, ());
TEST_EQUAL(it->second[1].m_userPos, ms::LatLon(53.016345, 158.683329), ());
TEST_EQUAL(it->second[1].m_type, MapObject::Event::Type::RouteToCreated, ());
}
{
MapObject poi;
poi.m_bestType = "shop";
poi.m_pos = {53.652005, 108.143448};
auto const it = mapObjects.find(poi);
TEST(it != mapObjects.end(), ());
TEST_EQUAL(it->second.size(), 2, ());
TEST_EQUAL(it->second[0].m_userPos, ms::LatLon(0.0, 0.0), ());
TEST_EQUAL(it->second[0].m_type, MapObject::Event::Type::RouteToCreated, ());
TEST_EQUAL(it->second[1].m_userPos, ms::LatLon(158.016345, 53.683329), ());
TEST_EQUAL(it->second[1].m_type, MapObject::Event::Type::AddToBookmark, ());
}
{
MapObject poi;
poi.m_bestType = "amenity-bench";
poi.m_pos = {53.652005, 108.143448};
auto const it = mapObjects.find(poi);
TEST(it != mapObjects.end(), ());
TEST_EQUAL(it->second.size(), 1, ());
TEST_EQUAL(it->second[0].m_userPos, ms::LatLon(0.0, 0.0), ());
TEST_EQUAL(it->second[0].m_type, MapObject::Event::Type::Open, ());
}
}
}

View file

@ -71,4 +71,17 @@ void EyeForTesting::AppendLayer(Layer::Type type)
{
Eye::Instance().RegisterLayerShown(type);
}
// static
void EyeForTesting::TrimExpiredMapObjectEvents()
{
Eye::Instance().TrimExpiredMapObjectEvents();
}
// static
void EyeForTesting::RegisterMapObjectEvent(MapObject const & mapObject, MapObject::Event::Type type,
ms::LatLon const & userPos)
{
Eye::Instance().RegisterMapObjectEvent(mapObject, type, userPos);
}
} // namespace eye

View file

@ -17,6 +17,9 @@ public:
static void UpdateDiscoveryShownTime();
static void IncrementDiscoveryItem(Discovery::Event event);
static void AppendLayer(Layer::Type type);
static void TrimExpiredMapObjectEvents();
static void RegisterMapObjectEvent(MapObject const & mapObject, MapObject::Event::Type type,
ms::LatLon const & userPos);
};
class ScopedEyeForTesting