diff --git a/search/search_quality/assessment_tool/context.cpp b/search/search_quality/assessment_tool/context.cpp index 862db33e68..f7c607dcda 100644 --- a/search/search_quality/assessment_tool/context.cpp +++ b/search/search_quality/assessment_tool/context.cpp @@ -12,8 +12,15 @@ using namespace std; // Context ----------------------------------------------------------------------------------------- void Context::Clear() { - m_results.Clear(); - m_edits.Clear(); + m_goldenMatching.clear(); + m_actualMatching.clear(); + + m_foundResults.Clear(); + m_foundResultsEdits.Clear(); + + m_nonFoundResults.clear(); + m_nonFoundResultsEdits.Clear(); + m_initialized = false; } @@ -24,17 +31,20 @@ search::Sample Context::MakeSample(search::FeatureLoader & loader) const if (!m_initialized) return outSample; - auto const & relevances = m_edits.GetRelevances(); + auto const & foundRelevances = m_foundResultsEdits.GetRelevances(); + auto const & nonFoundRelevances = m_nonFoundResultsEdits.GetRelevances(); auto & outResults = outSample.m_results; outResults.clear(); CHECK_EQUAL(m_goldenMatching.size(), m_sample.m_results.size(), ()); - CHECK_EQUAL(m_actualMatching.size(), relevances.size(), ()); - CHECK_EQUAL(m_actualMatching.size(), m_results.GetCount(), ()); + CHECK_EQUAL(m_actualMatching.size(), foundRelevances.size(), ()); + CHECK_EQUAL(m_actualMatching.size(), m_foundResults.GetCount(), ()); // Iterates over original (loaded from the file with search samples) // results first. + + size_t k = 0; for (size_t i = 0; i < m_sample.m_results.size(); ++i) { auto const j = m_goldenMatching[i]; @@ -43,21 +53,27 @@ search::Sample Context::MakeSample(search::FeatureLoader & loader) const // assessor. But we want to keep them. if (j == search::Matcher::kInvalidId) { - outResults.push_back(m_sample.m_results[i]); + auto const relevance = nonFoundRelevances[k++]; + if (relevance != search::Sample::Result::Relevance::Irrelevant) + { + auto result = m_sample.m_results[i]; + result.m_relevance = relevance; + outResults.push_back(result); + } continue; } // No need to keep irrelevant results. - if (relevances[j] == search::Sample::Result::Relevance::Irrelevant) + if (foundRelevances[j] == search::Sample::Result::Relevance::Irrelevant) continue; auto result = m_sample.m_results[i]; - result.m_relevance = relevances[j]; + result.m_relevance = foundRelevances[j]; outResults.push_back(move(result)); } // Iterates over results retrieved during assessment. - for (size_t i = 0; i < m_results.GetCount(); ++i) + for (size_t i = 0; i < m_foundResults.GetCount(); ++i) { auto const j = m_actualMatching[i]; if (j != search::Matcher::kInvalidId) @@ -67,17 +83,17 @@ search::Sample Context::MakeSample(search::FeatureLoader & loader) const } // No need to keep irrelevant results. - if (relevances[i] == search::Sample::Result::Relevance::Irrelevant) + if (foundRelevances[i] == search::Sample::Result::Relevance::Irrelevant) continue; - auto const & result = m_results.GetResult(i); + auto const & result = m_foundResults.GetResult(i); // No need in non-feature results. if (result.GetResultType() != search::Result::RESULT_FEATURE) continue; FeatureType ft; CHECK(loader.Load(result.GetFeatureID(), ft), ()); - outResults.push_back(search::Sample::Result::Build(ft, relevances[i])); + outResults.push_back(search::Sample::Result::Build(ft, foundRelevances[i])); } return outSample; @@ -87,11 +103,16 @@ void Context::ApplyEdits() { if (!m_initialized) return; - m_edits.ResetRelevances(m_edits.GetRelevances()); + m_foundResultsEdits.ResetRelevances(m_foundResultsEdits.GetRelevances()); + m_nonFoundResultsEdits.ResetRelevances(m_nonFoundResultsEdits.GetRelevances()); } // ContextList ------------------------------------------------------------------------------------- -ContextList::ContextList(OnUpdate onUpdate): m_onUpdate(onUpdate) {} +ContextList::ContextList(OnUpdate onResultsUpdate, OnUpdate onNonFoundResultsUpdate) + : m_onResultsUpdate(onResultsUpdate) + , m_onNonFoundResultsUpdate(onNonFoundResultsUpdate) +{ +} void ContextList::Resize(size_t size) { @@ -105,15 +126,17 @@ void ContextList::Resize(size_t size) m_hasChanges.resize(size); for (size_t i = oldSize; i < size; ++i) { - m_contexts.emplace_back([this, i](Edits::Update const & update) { - if (!m_hasChanges[i] && m_contexts[i].HasChanges()) - ++m_numChanges; - if (m_hasChanges[i] && !m_contexts[i].HasChanges()) - --m_numChanges; - m_hasChanges[i] = m_contexts[i].HasChanges(); - if (m_onUpdate) - m_onUpdate(i, update); - }); + m_contexts.emplace_back( + [this, i](Edits::Update const & update) { + OnContextUpdated(i); + if (m_onResultsUpdate) + m_onResultsUpdate(i, update); + }, + [this, i](Edits::Update const & update) { + OnContextUpdated(i); + if (m_onNonFoundResultsUpdate) + m_onNonFoundResultsUpdate(i, update); + }); } } @@ -130,3 +153,12 @@ void ContextList::ApplyEdits() for (auto & context : m_contexts) context.ApplyEdits(); } + +void ContextList::OnContextUpdated(size_t index) +{ + if (!m_hasChanges[index] && m_contexts[index].HasChanges()) + ++m_numChanges; + if (m_hasChanges[index] && !m_contexts[index].HasChanges()) + --m_numChanges; + m_hasChanges[index] = m_contexts[index].HasChanges(); +} diff --git a/search/search_quality/assessment_tool/context.hpp b/search/search_quality/assessment_tool/context.hpp index cfbcf744cf..e5c5548e11 100644 --- a/search/search_quality/assessment_tool/context.hpp +++ b/search/search_quality/assessment_tool/context.hpp @@ -18,9 +18,18 @@ class FeatureLoader; struct Context { - explicit Context(Edits::OnUpdate onUpdate) : m_edits(onUpdate) {} + Context(Edits::OnUpdate onFoundResultsUpdate, Edits::OnUpdate onNonFoundResultsUpdate) + : m_foundResultsEdits(onFoundResultsUpdate), m_nonFoundResultsEdits(onNonFoundResultsUpdate) + { + } + + bool HasChanges() const + { + if (!m_initialized) + return false; + return m_foundResultsEdits.HasChanges() || m_nonFoundResultsEdits.HasChanges(); + } - bool HasChanges() const { return m_initialized && m_edits.HasChanges(); } void Clear(); // Makes sample in accordance with uncommited edits. @@ -30,12 +39,15 @@ struct Context void ApplyEdits(); search::Sample m_sample; - search::Results m_results; - Edits m_edits; + search::Results m_foundResults; + Edits m_foundResultsEdits; std::vector m_goldenMatching; std::vector m_actualMatching; + std::vector m_nonFoundResults; + Edits m_nonFoundResultsEdits; + bool m_initialized = false; }; @@ -55,7 +67,7 @@ public: return strings::ToUtf8((*m_contexts)[index].m_sample.m_query); } - bool IsChanged(size_t index) const { return (*m_contexts)[index].m_edits.HasChanges(); } + bool IsChanged(size_t index) const { return (*m_contexts)[index].HasChanges(); } size_t Size() const { return m_contexts->Size(); } @@ -65,7 +77,7 @@ public: using OnUpdate = std::function; - explicit ContextList(OnUpdate onUpdate); + ContextList(OnUpdate onResultsUpdate, OnUpdate onNonFoundResultsUpdate); void Resize(size_t size); size_t Size() const { return m_contexts.size(); } @@ -82,9 +94,12 @@ public: void ApplyEdits(); private: + void OnContextUpdated(size_t index); + std::vector m_contexts; std::vector m_hasChanges; size_t m_numChanges = 0; - OnUpdate m_onUpdate; + OnUpdate m_onResultsUpdate; + OnUpdate m_onNonFoundResultsUpdate; }; diff --git a/search/search_quality/assessment_tool/main_model.cpp b/search/search_quality/assessment_tool/main_model.cpp index ace0f95bbc..70111a32fb 100644 --- a/search/search_quality/assessment_tool/main_model.cpp +++ b/search/search_quality/assessment_tool/main_model.cpp @@ -31,7 +31,13 @@ using namespace std; MainModel::MainModel(Framework & framework) : m_framework(framework) , m_index(m_framework.GetIndex()) - , m_contexts([this](size_t index, Edits::Update const & update) { OnUpdate(index, update); }) + , m_contexts( + [this](size_t sampleIndex, Edits::Update const & update) { + OnUpdate(View::ResultType::Found, sampleIndex, update); + }, + [this](size_t sampleIndex, Edits::Update const & update) { + OnUpdate(View::ResultType::NonFound, sampleIndex, update); + }) { } @@ -125,7 +131,7 @@ void MainModel::OnSampleSelected(int index) if (context.m_initialized) { - OnResults(timestamp, index, context.m_results, context.m_edits.GetRelevances(), + OnResults(timestamp, index, context.m_foundResults, context.m_foundResultsEdits.GetRelevances(), context.m_goldenMatching, context.m_actualMatching); return; } @@ -177,11 +183,23 @@ void MainModel::OnResultSelected(int index) CHECK_GREATER_OR_EQUAL(m_selectedSample, 0, ()); CHECK_LESS(m_selectedSample, m_contexts.Size(), ()); auto const & context = m_contexts[m_selectedSample]; - auto const & results = context.m_results; + auto const & foundResults = context.m_foundResults; CHECK_GREATER_OR_EQUAL(index, 0, ()); - CHECK_LESS(index, results.GetCount(), ()); - m_view->MoveViewportToResult(results.GetResult(index)); + CHECK_LESS(index, foundResults.GetCount(), ()); + m_view->MoveViewportToResult(foundResults.GetResult(index)); +} + +void MainModel::OnNonFoundResultSelected(int index) +{ + CHECK_GREATER_OR_EQUAL(m_selectedSample, 0, ()); + CHECK_LESS(m_selectedSample, m_contexts.Size(), ()); + auto const & context = m_contexts[m_selectedSample]; + auto const & results = context.m_nonFoundResults; + + CHECK_GREATER_OR_EQUAL(index, 0, ()); + CHECK_LESS(index, results.size(), ()); + m_view->MoveViewportToResult(results[index]); } void MainModel::OnShowViewportClicked() @@ -209,15 +227,17 @@ void MainModel::OnShowPositionClicked() bool MainModel::HasChanges() { return m_contexts.HasChanges(); } -void MainModel::OnUpdate(size_t index, Edits::Update const & update) +void MainModel::OnUpdate(View::ResultType type, size_t sampleIndex, Edits::Update const & update) { - CHECK_LESS(index, m_contexts.Size(), ()); - auto & context = m_contexts[index]; - m_view->OnSampleChanged(index, update, context.HasChanges()); + CHECK_LESS(sampleIndex, m_contexts.Size(), ()); + auto & context = m_contexts[sampleIndex]; + + m_view->OnResultChanged(sampleIndex, type, update); + m_view->OnSampleChanged(sampleIndex, context.HasChanges()); m_view->OnSamplesChanged(m_contexts.HasChanges()); } -void MainModel::OnResults(uint64_t timestamp, size_t index, search::Results const & results, +void MainModel::OnResults(uint64_t timestamp, size_t sampleIndex, search::Results const & results, vector const & relevances, vector const & goldenMatching, vector const & actualMatching) @@ -228,24 +248,48 @@ void MainModel::OnResults(uint64_t timestamp, size_t index, search::Results cons return; CHECK_LESS_OR_EQUAL(m_numShownResults, results.GetCount(), ()); - m_view->ShowResults(results.begin() + m_numShownResults, results.end()); + m_view->ShowFoundResults(results.begin() + m_numShownResults, results.end()); m_numShownResults = results.GetCount(); - auto & context = m_contexts[index]; - context.m_results = results; + auto & context = m_contexts[sampleIndex]; + context.m_foundResults = results; if (!results.IsEndedNormal()) return; if (!context.m_initialized) { - context.m_edits.ResetRelevances(relevances); + context.m_foundResultsEdits.ResetRelevances(relevances); context.m_goldenMatching = goldenMatching; context.m_actualMatching = actualMatching; + + { + vector relevances; + + auto & nonFound = context.m_nonFoundResults; + CHECK(nonFound.empty(), ()); + for (size_t i = 0; i < context.m_goldenMatching.size(); ++i) + { + auto const j = context.m_goldenMatching[i]; + if (j != search::Matcher::kInvalidId) + continue; + nonFound.push_back(context.m_sample.m_results[i]); + relevances.push_back(nonFound.back().m_relevance); + } + context.m_nonFoundResultsEdits.ResetRelevances(relevances); + } + context.m_initialized = true; } - m_view->OnSampleChanged(index, Edits::Update::AllRelevancesUpdate(), context.HasChanges()); - m_view->EnableSampleEditing(index, context.m_edits); + + m_view->ShowNonFoundResults(context.m_nonFoundResults); + m_view->OnResultChanged(sampleIndex, View::ResultType::Found, + Edits::Update::AllRelevancesUpdate()); + m_view->OnResultChanged(sampleIndex, View::ResultType::NonFound, + Edits::Update::AllRelevancesUpdate()); + m_view->OnSampleChanged(sampleIndex, context.HasChanges()); + m_view->EnableSampleEditing(sampleIndex, context.m_foundResultsEdits, + context.m_nonFoundResultsEdits); } void MainModel::ResetSearch() diff --git a/search/search_quality/assessment_tool/main_model.hpp b/search/search_quality/assessment_tool/main_model.hpp index cbff49574d..69c9cc5774 100644 --- a/search/search_quality/assessment_tool/main_model.hpp +++ b/search/search_quality/assessment_tool/main_model.hpp @@ -4,6 +4,7 @@ #include "search/search_quality/assessment_tool/context.hpp" #include "search/search_quality/assessment_tool/edits.hpp" #include "search/search_quality/assessment_tool/model.hpp" +#include "search/search_quality/assessment_tool/view.hpp" #include "search/search_quality/sample.hpp" #include "base/thread_checker.hpp" @@ -32,6 +33,7 @@ public: void OnSampleSelected(int index) override; void OnResultSelected(int index) override; + void OnNonFoundResultSelected(int index) override; void OnShowViewportClicked() override; void OnShowPositionClicked() override; bool HasChanges() override; @@ -39,9 +41,9 @@ public: private: static int constexpr kInvalidIndex = -1; - void OnUpdate(size_t index, Edits::Update const & update); + void OnUpdate(View::ResultType type, size_t sampleIndex, Edits::Update const & update); - void OnResults(uint64_t timestamp, size_t index, search::Results const & results, + void OnResults(uint64_t timestamp, size_t sampleIndex, search::Results const & results, std::vector const & relevances, std::vector const & goldenMatching, std::vector const & actualMatching); diff --git a/search/search_quality/assessment_tool/main_view.cpp b/search/search_quality/assessment_tool/main_view.cpp index d5b2bbc7fc..67b034894d 100644 --- a/search/search_quality/assessment_tool/main_view.cpp +++ b/search/search_quality/assessment_tool/main_view.cpp @@ -11,6 +11,8 @@ #include "map/framework.hpp" +#include "geometry/mercator.hpp" + #include "base/assert.hpp" #include "base/string_utils.hpp" @@ -58,19 +60,26 @@ void MainView::SetSamples(ContextList::SamplesSlice const & samples) m_sampleView->Clear(); } -void MainView::ShowSample(size_t index, search::Sample const & sample, bool hasEdits) +void MainView::ShowSample(size_t sampleIndex, search::Sample const & sample, bool hasEdits) { MoveViewportToRect(sample.m_viewport); m_sampleView->SetContents(sample); m_sampleView->show(); - OnSampleChanged(index, Edits::Update::AllRelevancesUpdate(), hasEdits); + OnResultChanged(sampleIndex, ResultType::Found, Edits::Update::AllRelevancesUpdate()); + OnResultChanged(sampleIndex, ResultType::NonFound, Edits::Update::AllRelevancesUpdate()); + OnSampleChanged(sampleIndex, hasEdits); } -void MainView::ShowResults(search::Results::Iter begin, search::Results::Iter end) +void MainView::ShowFoundResults(search::Results::Iter begin, search::Results::Iter end) { - m_sampleView->ShowResults(begin, end); + m_sampleView->ShowFoundResults(begin, end); +} + +void MainView::ShowNonFoundResults(std::vector const & results) +{ + m_sampleView->ShowNonFoundResults(results); } void MainView::MoveViewportToResult(search::Result const & result) @@ -78,18 +87,36 @@ void MainView::MoveViewportToResult(search::Result const & result) m_framework.ShowSearchResult(result, false /* animation */); } +void MainView::MoveViewportToResult(search::Sample::Result const & result) { + int constexpr kViewportAroundResultSizeM = 100; + auto const rect = + MercatorBounds::RectByCenterXYAndSizeInMeters(result.m_pos, kViewportAroundResultSizeM); + MoveViewportToRect(rect); +} + void MainView::MoveViewportToRect(m2::RectD const & rect) { m_framework.ShowRect(rect, -1 /* maxScale */, false /* animation */); } -void MainView::OnSampleChanged(size_t index, Edits::Update const & update, bool hasEdits) +void MainView::OnResultChanged(size_t sampleIndex, ResultType type, Edits::Update const & update) { - m_samplesView->OnUpdate(index); - if (!m_samplesView->IsSelected(index)) + m_samplesView->OnUpdate(sampleIndex); + + if (!m_samplesView->IsSelected(sampleIndex)) + return; + switch (type) + { + case ResultType::Found: m_sampleView->GetFoundResultsView().Update(update); break; + case ResultType::NonFound: m_sampleView->GetNonFoundResultsView().Update(update); break; + } +} + +void MainView::OnSampleChanged(size_t sampleIndex, bool hasEdits) +{ + if (!m_samplesView->IsSelected(sampleIndex)) return; SetSampleDockTitle(hasEdits); - m_sampleView->Update(update); } void MainView::OnSamplesChanged(bool hasEdits) @@ -99,10 +126,11 @@ void MainView::OnSamplesChanged(bool hasEdits) m_saveAs->setEnabled(hasEdits); } -void MainView::EnableSampleEditing(size_t index, Edits & edits) +void MainView::EnableSampleEditing(size_t sampleIndex, Edits & foundResultsEdits, + Edits & nonFoundResultsEdits) { - CHECK(m_samplesView->IsSelected(index), ()); - m_sampleView->EnableEditing(edits); + CHECK(m_samplesView->IsSelected(sampleIndex), ()); + m_sampleView->EnableEditing(foundResultsEdits, nonFoundResultsEdits); } void MainView::ShowError(std::string const & msg) @@ -146,6 +174,14 @@ void MainView::OnResultSelected(QItemSelection const & current) m_model->OnResultSelected(index.row()); } +void MainView::OnNonFoundResultSelected(QItemSelection const & current) +{ + CHECK(m_model, ()); + auto const indexes = current.indexes(); + for (auto const & index : indexes) + m_model->OnNonFoundResultSelected(index.row()); +} + void MainView::InitMenuBar() { auto * bar = menuBar(); @@ -242,8 +278,15 @@ void MainView::InitDocks() [this]() { m_model->OnShowPositionClicked(); }); { - auto * model = m_sampleView->GetResultsView().selectionModel(); - connect(model, &QItemSelectionModel::selectionChanged, this, &MainView::OnResultSelected); + auto const & view = m_sampleView->GetFoundResultsView(); + connect(&view, &ResultsView::OnResultSelected, + [&](int index) { m_model->OnResultSelected(index); }); + } + + { + auto const & view = m_sampleView->GetNonFoundResultsView(); + connect(&view, &ResultsView::OnResultSelected, + [&](int index) { m_model->OnNonFoundResultSelected(index); }); } m_sampleDock = CreateDock(*m_sampleView); diff --git a/search/search_quality/assessment_tool/main_view.hpp b/search/search_quality/assessment_tool/main_view.hpp index 16d26fcefd..1f3b882d8e 100644 --- a/search/search_quality/assessment_tool/main_view.hpp +++ b/search/search_quality/assessment_tool/main_view.hpp @@ -28,14 +28,18 @@ public: // View overrides: void SetSamples(ContextList::SamplesSlice const & samples) override; - void ShowSample(size_t index, search::Sample const & sample, bool hasEdits) override; - void ShowResults(search::Results::Iter begin, search::Results::Iter end) override; + void ShowSample(size_t sampleIndex, search::Sample const & sample, bool hasEdits) override; + void ShowFoundResults(search::Results::Iter begin, search::Results::Iter end) override; + void ShowNonFoundResults(std::vector const & results) override; void MoveViewportToResult(search::Result const & result) override; + void MoveViewportToResult(search::Sample::Result const & result) override; void MoveViewportToRect(m2::RectD const & rect) override; - void OnSampleChanged(size_t index, Edits::Update const & update, bool hasEdits) override; - void EnableSampleEditing(size_t index, Edits & edits) override; + void OnResultChanged(size_t sampleIndex, ResultType type, Edits::Update const & update) override; + void EnableSampleEditing(size_t sampleIndex, Edits & foundResultsEdits, + Edits & nonFoundResultsEdits) override; + void OnSampleChanged(size_t sampleIndex, bool hasEdits) override; void OnSamplesChanged(bool hasEdits) override; void ShowError(std::string const & msg) override; @@ -49,6 +53,7 @@ protected: private slots: void OnSampleSelected(QItemSelection const & current); void OnResultSelected(QItemSelection const & current); + void OnNonFoundResultSelected(QItemSelection const & current); private: enum class SaveResult diff --git a/search/search_quality/assessment_tool/model.hpp b/search/search_quality/assessment_tool/model.hpp index 11ff4df73f..e4746d46b6 100644 --- a/search/search_quality/assessment_tool/model.hpp +++ b/search/search_quality/assessment_tool/model.hpp @@ -17,6 +17,7 @@ public: virtual void OnSampleSelected(int index) = 0; virtual void OnResultSelected(int index) = 0; + virtual void OnNonFoundResultSelected(int index) = 0; virtual void OnShowViewportClicked() = 0; virtual void OnShowPositionClicked() = 0; virtual bool HasChanges() = 0; diff --git a/search/search_quality/assessment_tool/result_view.cpp b/search/search_quality/assessment_tool/result_view.cpp index d672aa6743..ac82239ee8 100644 --- a/search/search_quality/assessment_tool/result_view.cpp +++ b/search/search_quality/assessment_tool/result_view.cpp @@ -5,14 +5,16 @@ #include "base/stl_add.hpp" -#include #include +#include #include #include #include #include +using namespace std; + namespace { QLabel * CreateLabel(QWidget & parent) @@ -23,7 +25,7 @@ QLabel * CreateLabel(QWidget & parent) return label; } -void SetText(QLabel & label, std::string const & text) { +void SetText(QLabel & label, string const & text) { if (text.empty()) { label.hide(); @@ -33,16 +35,34 @@ void SetText(QLabel & label, std::string const & text) { label.setText(ToQString(text)); label.show(); } + +string GetResultType(search::Sample::Result const & result) +{ + return strings::JoinStrings(result.m_types, ", "); +} } // namespace -ResultView::ResultView(search::Result const & result, QWidget & parent) : QWidget(&parent) +ResultView::ResultView(string const & name, string const & type, string const & address, + QWidget & parent) + : QWidget(&parent) { Init(); - SetContents(result); + SetContents(name, type, address); setEnabled(false); setObjectName("result"); } +ResultView::ResultView(search::Result const & result, QWidget & parent) + : ResultView(result.GetString(), result.GetFeatureType(), result.GetAddress(), parent) +{ +} + +ResultView::ResultView(search::Sample::Result const & result, QWidget & parent) + : ResultView(strings::ToUtf8(result.m_name), GetResultType(result), string() /* address */, + parent) +{ +} + void ResultView::EnableEditing(Edits::RelevanceEditor && editor) { m_editor = my::make_unique(std::move(editor)); @@ -75,8 +95,8 @@ void ResultView::Init() layout->setSizeConstraint(QLayout::SetMinimumSize); setLayout(layout); - m_string = CreateLabel(*this /* parent */); - layout->addWidget(m_string); + m_name = CreateLabel(*this /* parent */); + layout->addWidget(m_name); m_type = CreateLabel(*this /* parent */); m_type->setStyleSheet("QLabel { font-size : 8pt }"); @@ -99,11 +119,11 @@ void ResultView::Init() } } -void ResultView::SetContents(search::Result const & result) +void ResultView::SetContents(string const & name, string const & type, string const & address) { - SetText(*m_string, result.GetString()); - SetText(*m_type, result.GetFeatureType()); - SetText(*m_address, result.GetAddress()); + SetText(*m_name, name); + SetText(*m_type, type); + SetText(*m_address, address); m_irrelevant->setChecked(false); m_relevant->setChecked(false); diff --git a/search/search_quality/assessment_tool/result_view.hpp b/search/search_quality/assessment_tool/result_view.hpp index 4df28b80ed..47b7ebac29 100644 --- a/search/search_quality/assessment_tool/result_view.hpp +++ b/search/search_quality/assessment_tool/result_view.hpp @@ -4,6 +4,7 @@ #include "search/search_quality/sample.hpp" #include +#include #include @@ -21,19 +22,24 @@ public: using Relevance = search::Sample::Result::Relevance; ResultView(search::Result const & result, QWidget & parent); + ResultView(search::Sample::Result const & result, QWidget & parent); void EnableEditing(Edits::RelevanceEditor && editor); void Update(); private: + ResultView(std::string const & name, std::string const & type, std::string const & address, + QWidget & parent); + void Init(); - void SetContents(search::Result const & result); + void SetContents(std::string const & name, std::string const & type, + std::string const & address); QRadioButton * CreateRatioButton(string const & label, QLayout & layout); void OnRelevanceChanged(); - QLabel * m_string = nullptr; + QLabel * m_name = nullptr; QLabel * m_type = nullptr; QLabel * m_address = nullptr; diff --git a/search/search_quality/assessment_tool/results_view.cpp b/search/search_quality/assessment_tool/results_view.cpp index 7e6967abe9..2a2ca6f09c 100644 --- a/search/search_quality/assessment_tool/results_view.cpp +++ b/search/search_quality/assessment_tool/results_view.cpp @@ -6,18 +6,27 @@ #include -ResultsView::ResultsView(QWidget & parent) : QListWidget(&parent) { setAlternatingRowColors(true); } +ResultsView::ResultsView(QWidget & parent) : QListWidget(&parent) { setAlternatingRowColors(true); + connect(selectionModel(), &QItemSelectionModel::selectionChanged, + [&](QItemSelection const & current) { + auto const indexes = current.indexes(); + for (auto const & index : indexes) + emit OnResultSelected(index.row()); + }); + connect(this, &ResultsView::itemClicked, [&](QListWidgetItem * item) { + auto const index = indexFromItem(item); + emit OnResultSelected(index.row()); + }); +} void ResultsView::Add(search::Result const & result) { - auto * item = new QListWidgetItem(this /* parent */); - addItem(item); + AddImpl(result); +} - auto * view = new ResultView(result, *this /* parent */); - item->setSizeHint(view->minimumSizeHint()); - setItemWidget(item, view); - - m_results.push_back(view); +void ResultsView::Add(search::Sample::Result const & result) +{ + AddImpl(result); } ResultView & ResultsView::Get(size_t i) @@ -52,3 +61,16 @@ void ResultsView::Clear() m_results.clear(); clear(); } + +template +void ResultsView::AddImpl(Result const & result) +{ + auto * item = new QListWidgetItem(this /* parent */); + addItem(item); + + auto * view = new ResultView(result, *this /* parent */); + item->setSizeHint(view->minimumSizeHint()); + setItemWidget(item, view); + + m_results.push_back(view); +} diff --git a/search/search_quality/assessment_tool/results_view.hpp b/search/search_quality/assessment_tool/results_view.hpp index a2f08ffd44..f053b7d5a7 100644 --- a/search/search_quality/assessment_tool/results_view.hpp +++ b/search/search_quality/assessment_tool/results_view.hpp @@ -17,10 +17,13 @@ class Result; class ResultsView : public QListWidget { + Q_OBJECT + public: explicit ResultsView(QWidget & parent); void Add(search::Result const & result); + void Add(search::Sample::Result const & result); ResultView & Get(size_t i); ResultView const & Get(size_t i) const; @@ -30,6 +33,12 @@ public: void Clear(); +signals: + void OnResultSelected(int index); + private: + template + void AddImpl(Result const & result); + std::vector m_results; }; diff --git a/search/search_quality/assessment_tool/sample_view.cpp b/search/search_quality/assessment_tool/sample_view.cpp index 9a4004defd..50d10f2d3e 100644 --- a/search/search_quality/assessment_tool/sample_view.cpp +++ b/search/search_quality/assessment_tool/sample_view.cpp @@ -72,10 +72,18 @@ SampleView::SampleView(QWidget * parent) : QWidget(parent) layout->addWidget(new QLabel(tr("Found results"))); - m_results = new ResultsView(*this /* parent */); - layout->addWidget(m_results); + m_foundResults = new ResultsView(*this /* parent */); + layout->addWidget(m_foundResults); } + { + auto * layout = BuildSubLayout(*mainLayout, *this /* parent */); + + layout->addWidget(new QLabel(tr("Non found results"))); + + m_nonFoundResults = new ResultsView(*this /* parent */); + layout->addWidget(m_nonFoundResults); + } setLayout(mainLayout); Clear(); @@ -90,26 +98,27 @@ void SampleView::SetContents(search::Sample const & sample) m_showViewport->setEnabled(true); m_showPosition->setEnabled(true); - m_results->Clear(); + m_foundResults->Clear(); + m_nonFoundResults->Clear(); } -void SampleView::ShowResults(search::Results::Iter begin, search::Results::Iter end) +void SampleView::ShowFoundResults(search::Results::Iter begin, search::Results::Iter end) { for (auto it = begin; it != end; ++it) - m_results->Add(*it /* result */); + m_foundResults->Add(*it /* result */); } -void SampleView::EnableEditing(Edits & edits) +void SampleView::ShowNonFoundResults(std::vector const & results) { - m_edits = &edits; - - size_t const numRelevances = m_edits->GetRelevances().size(); - CHECK_EQUAL(m_results->Size(), numRelevances, ()); - for (size_t i = 0; i < numRelevances; ++i) - m_results->Get(i).EnableEditing(Edits::RelevanceEditor(*m_edits, i)); + for (auto const & result : results) + m_nonFoundResults->Add(result); } -void SampleView::Update(Edits::Update const & update) { m_results->Update(update); } +void SampleView::EnableEditing(Edits & resultsEdits, Edits & nonFoundResultsEdits) +{ + EnableEditing(*m_foundResults, resultsEdits); + EnableEditing(*m_nonFoundResults, nonFoundResultsEdits); +} void SampleView::Clear() { @@ -117,6 +126,14 @@ void SampleView::Clear() m_langs->Select("default"); m_showViewport->setEnabled(false); m_showPosition->setEnabled(false); - m_results->Clear(); - m_edits = nullptr; + m_foundResults->Clear(); + m_nonFoundResults->Clear(); +} + +void SampleView::EnableEditing(ResultsView & results, Edits & edits) +{ + size_t const numRelevances = edits.GetRelevances().size(); + CHECK_EQUAL(results.Size(), numRelevances, ()); + for (size_t i = 0; i < numRelevances; ++i) + results.Get(i).EnableEditing(Edits::RelevanceEditor(edits, i)); } diff --git a/search/search_quality/assessment_tool/sample_view.hpp b/search/search_quality/assessment_tool/sample_view.hpp index f4c52cc2f5..ca12160199 100644 --- a/search/search_quality/assessment_tool/sample_view.hpp +++ b/search/search_quality/assessment_tool/sample_view.hpp @@ -21,25 +21,27 @@ public: explicit SampleView(QWidget * parent); void SetContents(search::Sample const & sample); - void ShowResults(search::Results::Iter begin, search::Results::Iter end); + void ShowFoundResults(search::Results::Iter begin, search::Results::Iter end); + void ShowNonFoundResults(std::vector const & results); - void EnableEditing(Edits & edits); + void EnableEditing(Edits & resultsEdits, Edits & nonFoundResultsEdits); - void Update(Edits::Update const & update); void Clear(); - ResultsView & GetResultsView() { return *m_results; } + ResultsView & GetFoundResultsView() { return *m_foundResults; } + ResultsView & GetNonFoundResultsView() { return *m_nonFoundResults; } signals: void OnShowViewportClicked(); void OnShowPositionClicked(); private: + void EnableEditing(ResultsView & results, Edits & edits); + QLineEdit * m_query = nullptr; LanguagesList * m_langs = nullptr; QPushButton * m_showViewport = nullptr; QPushButton * m_showPosition = nullptr; - ResultsView * m_results = nullptr; - - Edits * m_edits = nullptr; + ResultsView * m_foundResults = nullptr; + ResultsView * m_nonFoundResults = nullptr; }; diff --git a/search/search_quality/assessment_tool/view.hpp b/search/search_quality/assessment_tool/view.hpp index 77b7affb75..15cf4a044d 100644 --- a/search/search_quality/assessment_tool/view.hpp +++ b/search/search_quality/assessment_tool/view.hpp @@ -3,6 +3,7 @@ #include "search/result.hpp" #include "search/search_quality/assessment_tool/context.hpp" #include "search/search_quality/assessment_tool/edits.hpp" +#include "search/search_quality/sample.hpp" #include "geometry/rect2d.hpp" @@ -16,19 +17,30 @@ class Model; class View { public: + enum class ResultType + { + Found, + NonFound + }; + virtual ~View() = default; void SetModel(Model & model) { m_model = &model; } virtual void SetSamples(ContextList::SamplesSlice const & samples) = 0; virtual void ShowSample(size_t index, search::Sample const & sample, bool hasEdits) = 0; - virtual void ShowResults(search::Results::Iter begin, search::Results::Iter end) = 0; + virtual void ShowFoundResults(search::Results::Iter begin, search::Results::Iter end) = 0; + virtual void ShowNonFoundResults(std::vector const & results) = 0; virtual void MoveViewportToResult(search::Result const & result) = 0; + virtual void MoveViewportToResult(search::Sample::Result const & result) = 0; virtual void MoveViewportToRect(m2::RectD const & rect) = 0; - virtual void OnSampleChanged(size_t index, Edits::Update const & update, bool hasEdits) = 0; - virtual void EnableSampleEditing(size_t index, Edits & edits) = 0; + virtual void OnResultChanged(size_t sampleIndex, ResultType type, + Edits::Update const & update) = 0; + virtual void EnableSampleEditing(size_t index, Edits & foundResultsEdits, + Edits & nonFoundResultsEdits) = 0; + virtual void OnSampleChanged(size_t sampleIndex, bool hasEdits) = 0; virtual void OnSamplesChanged(bool hasEdits) = 0; virtual void ShowError(std::string const & msg) = 0;