diff --git a/qt/qt_common/map_widget.cpp b/qt/qt_common/map_widget.cpp index bd2746a890..0ddc9d60c6 100644 --- a/qt/qt_common/map_widget.cpp +++ b/qt/qt_common/map_widget.cpp @@ -356,6 +356,9 @@ void MapWidget::mouseMoveEvent(QMouseEvent * e) void MapWidget::mouseReleaseEvent(QMouseEvent * e) { + if (e->button() == Qt::RightButton) + emit OnContextMenuRequested(e->globalPos()); + QOpenGLWidget::mouseReleaseEvent(e); if (IsLeftButton(e)) m_framework.TouchEvent(GetTouchEvent(e, df::TouchEvent::TOUCH_UP)); diff --git a/qt/qt_common/map_widget.hpp b/qt/qt_common/map_widget.hpp index 4956b592a8..a238532e88 100644 --- a/qt/qt_common/map_widget.hpp +++ b/qt/qt_common/map_widget.hpp @@ -36,7 +36,10 @@ public: void BindSlider(ScaleSlider & slider); void CreateEngine(); -public Q_SLOTS: +signals: + void OnContextMenuRequested(QPoint const & p); + +public slots: void ScalePlus(); void ScaleMinus(); void ScalePlusLight(); diff --git a/search/search_quality/assessment_tool/context.cpp b/search/search_quality/assessment_tool/context.cpp index 5704f1e56c..228ae37477 100644 --- a/search/search_quality/assessment_tool/context.cpp +++ b/search/search_quality/assessment_tool/context.cpp @@ -49,8 +49,6 @@ search::Sample Context::MakeSample(search::FeatureLoader & loader) const { auto const j = m_goldenMatching[i]; - // Some results weren't matched, so they weren't displayed to the - // assessor. But we want to keep them. if (j == search::Matcher::kInvalidId) { auto const & entry = nonFoundEntries[k++]; diff --git a/search/search_quality/assessment_tool/context.hpp b/search/search_quality/assessment_tool/context.hpp index e5c5548e11..26cd3c03d2 100644 --- a/search/search_quality/assessment_tool/context.hpp +++ b/search/search_quality/assessment_tool/context.hpp @@ -2,6 +2,7 @@ #include "search/result.hpp" #include "search/search_quality/assessment_tool/edits.hpp" +#include "search/search_quality/matcher.hpp" #include "search/search_quality/sample.hpp" #include "base/string_utils.hpp" @@ -23,6 +24,17 @@ struct Context { } + void AddNonFoundResult(search::Sample::Result const & result) + { + CHECK_EQUAL(m_goldenMatching.size(), m_sample.m_results.size(), ()); + + m_sample.m_results.push_back(result); + m_goldenMatching.push_back(search::Matcher::kInvalidId); + + m_nonFoundResults.push_back(result); + m_nonFoundResultsEdits.Add(result.m_relevance); + } + bool HasChanges() const { if (!m_initialized) diff --git a/search/search_quality/assessment_tool/edits.cpp b/search/search_quality/assessment_tool/edits.cpp index 62df175cb2..b9ae900132 100644 --- a/search/search_quality/assessment_tool/edits.cpp +++ b/search/search_quality/assessment_tool/edits.cpp @@ -3,29 +3,37 @@ #include "base/assert.hpp" // Edits::RelevanceEditor -------------------------------------------------------------------------- -Edits::RelevanceEditor::RelevanceEditor(Edits & parent, size_t index) +Edits::Editor::Editor(Edits & parent, size_t index) : m_parent(parent), m_index(index) { } -bool Edits::RelevanceEditor::Set(Relevance relevance) +bool Edits::Editor::Set(Relevance relevance) { return m_parent.SetRelevance(m_index, relevance); } -Edits::Relevance Edits::RelevanceEditor::Get() const +Edits::Relevance Edits::Editor::Get() const { return m_parent.Get(m_index).m_curr; } -bool Edits::RelevanceEditor::HasChanges() const { return m_parent.HasChanges(m_index); } +bool Edits::Editor::HasChanges() const { return m_parent.HasChanges(m_index); } + +Edits::Entry::Origin Edits::Editor::GetOrigin() const +{ + return m_parent.Get(m_index).m_origin; +} // Edits ------------------------------------------------------------------------------------------- void Edits::Apply() { WithObserver(Update::MakeAll(), [this]() { for (auto & entry : m_entries) + { entry.m_orig = entry.m_curr; + entry.m_origin = Entry::Origin::Loaded; + } m_numEdits = 0; }); } @@ -34,11 +42,13 @@ void Edits::Reset(std::vector const & relevances) { WithObserver(Update::MakeAll(), [this, &relevances]() { m_entries.resize(relevances.size()); - for (size_t i = 0; i < relevances.size(); ++i) + for (size_t i = 0; i < m_entries.size(); ++i) { - m_entries[i].m_orig = relevances[i]; - m_entries[i].m_curr = relevances[i]; - m_entries[i].m_deleted = false; + auto & entry = m_entries[i]; + entry.m_orig = relevances[i]; + entry.m_curr = relevances[i]; + entry.m_deleted = false; + entry.m_origin = Entry::Origin::Loaded; } m_numEdits = 0; }); @@ -61,6 +71,15 @@ bool Edits::SetRelevance(size_t index, Relevance relevance) }); } +void Edits::Add(Relevance relevance) +{ + auto const index = m_entries.size(); + WithObserver(Update::MakeAdd(index), [&]() { + m_entries.emplace_back(relevance, Entry::Origin::Created); + ++m_numEdits; + }); +} + void Edits::Delete(size_t index) { return WithObserver(Update::MakeDelete(index), [this, index]() { @@ -69,10 +88,43 @@ void Edits::Delete(size_t index) auto & entry = m_entries[index]; CHECK(!entry.m_deleted, ()); entry.m_deleted = true; - ++m_numEdits; + switch (entry.m_origin) + { + case Entry::Origin::Loaded: ++m_numEdits; break; + case Entry::Origin::Created: --m_numEdits; break; + } }); } +void Edits::Resurrect(size_t index) +{ + return WithObserver(Update::MakeResurrect(index), [this, index]() { + CHECK_LESS(index, m_entries.size(), ()); + + auto & entry = m_entries[index]; + CHECK(entry.m_deleted, ()); + CHECK_GREATER(m_numEdits, 0, ()); + entry.m_deleted = false; + switch (entry.m_origin) + { + case Entry::Origin::Loaded: --m_numEdits; break; + case Entry::Origin::Created: ++m_numEdits; break; + } + }); +} + +Edits::Entry & Edits::GetEntry(size_t index) +{ + CHECK_LESS(index, m_entries.size(), ()); + return m_entries[index]; +} + +Edits::Entry const & Edits::GetEntry(size_t index) const +{ + CHECK_LESS(index, m_entries.size(), ()); + return m_entries[index]; +} + std::vector Edits::GetRelevances() const { std::vector relevances(m_entries.size()); @@ -101,5 +153,6 @@ bool Edits::HasChanges(size_t index) const { CHECK_LESS(index, m_entries.size(), ()); auto const & entry = m_entries[index]; - return entry.m_curr != entry.m_orig; + bool result = entry.m_curr != entry.m_orig; + return result; } diff --git a/search/search_quality/assessment_tool/edits.hpp b/search/search_quality/assessment_tool/edits.hpp index 8a1d9e5ace..e15d00c6c7 100644 --- a/search/search_quality/assessment_tool/edits.hpp +++ b/search/search_quality/assessment_tool/edits.hpp @@ -17,11 +17,22 @@ public: struct Entry { + enum class Origin + { + Loaded, + Created + }; + Entry() = default; + Entry(Relevance relevance, Origin origin) + : m_curr(relevance), m_orig(relevance), m_origin(origin) + { + } Relevance m_curr = Relevance::Irrelevant; Relevance m_orig = Relevance::Irrelevant; bool m_deleted = false; + Origin m_origin = Origin::Loaded; }; struct Update @@ -32,17 +43,19 @@ public: { Single, All, - Delete + Add, + Delete, + Resurrect }; Update() = default; Update(Type type, size_t index): m_type(type), m_index(index) {} static Update MakeAll() { return Update{}; } - static Update MakeSingle(size_t index) { return Update{Type::Single, index}; } - + static Update MakeAdd(size_t index) { return Update{Type::Add, index}; } static Update MakeDelete(size_t index) { return Update{Type::Delete, index}; } + static Update MakeResurrect(size_t index) { return Update{Type::Resurrect, index}; } Type m_type = Type::All; size_t m_index = kInvalidIndex; @@ -50,16 +63,17 @@ public: using OnUpdate = std::function; - class RelevanceEditor + class Editor { public: - RelevanceEditor(Edits & parent, size_t index); + Editor(Edits & parent, size_t index); // Sets relevance to |relevance|. Returns true iff |relevance| // differs from the original one. bool Set(Relevance relevance); Relevance Get() const; bool HasChanges() const; + Entry::Origin GetOrigin() const; private: Edits & m_parent; @@ -75,10 +89,19 @@ public: // |relevance| differs from the original one. bool SetRelevance(size_t index, Relevance relevance); + // Addes new entry. + void Add(Relevance relevance); + // Marks entry at |index| as deleted. void Delete(size_t index); + // Resurrects previously deleted entry at |index|. + void Resurrect(size_t index); + std::vector const & GetEntries() const { return m_entries; } + Entry & GetEntry(size_t index); + Entry const & GetEntry(size_t index) const; + size_t NumEntries() const { return m_entries.size(); } std::vector GetRelevances() const; Entry const & Get(size_t index) const; diff --git a/search/search_quality/assessment_tool/main_model.cpp b/search/search_quality/assessment_tool/main_model.cpp index 37b90764e2..777557fbb9 100644 --- a/search/search_quality/assessment_tool/main_model.cpp +++ b/search/search_quality/assessment_tool/main_model.cpp @@ -31,6 +31,7 @@ using namespace std; MainModel::MainModel(Framework & framework) : m_framework(framework) , m_index(m_framework.GetIndex()) + , m_loader(m_index) , m_contexts( [this](size_t sampleIndex, Edits::Update const & update) { OnUpdate(View::ResultType::Found, sampleIndex, update); @@ -92,10 +93,8 @@ void MainModel::SaveAs(string const & path) CHECK(HasChanges(), ()); CHECK(!path.empty(), ()); - search::FeatureLoader loader(m_index); - string contents; - search::Sample::SerializeToJSONLines(m_contexts.MakeSamples(loader), contents); + search::Sample::SerializeToJSONLines(m_contexts.MakeSamples(m_loader), contents); { ofstream ofs(path); @@ -147,6 +146,7 @@ void MainModel::OnSampleSelected(int index) if (results.IsEndedNormal()) { + // Can't use m_loader here due to thread-safety issues. search::FeatureLoader loader(m_index); search::Matcher matcher(loader); @@ -224,19 +224,70 @@ void MainModel::OnShowPositionClicked() bool MainModel::HasChanges() { return m_contexts.HasChanges(); } +bool MainModel::AlreadyInSamples(FeatureID const & id) +{ + CHECK(m_selectedSample != kInvalidIndex, ()); + CHECK(m_selectedSample < m_contexts.Size(), ()); + + bool found = false; + ForMatchingEntries(m_contexts[m_selectedSample], id, [&](Edits & edits, size_t index) + { + auto const & entry = edits.GetEntry(index); + if (!entry.m_deleted) + found = true; + }); + return found; +} + +void MainModel::AddNonFoundResult(FeatureID const & id) +{ + CHECK(m_selectedSample != kInvalidIndex, ()); + CHECK(m_selectedSample < m_contexts.Size(), ()); + + auto & context = m_contexts[m_selectedSample]; + + bool resurrected = false; + ForMatchingEntries(context, id, [&](Edits & edits, size_t index) + { + auto const & entry = edits.GetEntry(index); + CHECK(entry.m_deleted, ()); + edits.Resurrect(index); + resurrected = true; + }); + if (resurrected) + return; + + { + FeatureType ft; + CHECK(m_loader.Load(id, ft), ("Can't load feature:", id)); + auto const result = search::Sample::Result::Build(ft, search::Sample::Result::Relevance::Vital); + context.AddNonFoundResult(result); + } +} + void MainModel::OnUpdate(View::ResultType type, size_t sampleIndex, Edits::Update const & update) { + using Type = Edits::Update::Type; + CHECK_LESS(sampleIndex, m_contexts.Size(), ()); auto & context = m_contexts[sampleIndex]; + if (update.m_type == Type::Add) + { + CHECK_EQUAL(type, View::ResultType::NonFound, ()); + m_view->ShowNonFoundResults(context.m_nonFoundResults, + context.m_nonFoundResultsEdits.GetEntries()); + m_view->SetEdits(m_selectedSample, context.m_foundResultsEdits, context.m_nonFoundResultsEdits); + } + m_view->OnResultChanged(sampleIndex, type, update); m_view->OnSampleChanged(sampleIndex, context.HasChanges()); m_view->OnSamplesChanged(m_contexts.HasChanges()); - if (update.m_type == Edits::Update::Type::Delete) + if (update.m_type == Type::Add || update.m_type == Type::Resurrect || + update.m_type == Type::Delete) { CHECK(context.m_initialized, ()); - CHECK_EQUAL(type, View::ResultType::NonFound, ()); ShowMarks(context); } @@ -253,7 +304,7 @@ void MainModel::OnResults(uint64_t timestamp, size_t sampleIndex, search::Result return; CHECK_LESS_OR_EQUAL(m_numShownResults, results.GetCount(), ()); - m_view->ShowFoundResults(results.begin() + m_numShownResults, results.end()); + m_view->AddFoundResults(results.begin() + m_numShownResults, results.end()); m_numShownResults = results.GetCount(); auto & context = m_contexts[sampleIndex]; @@ -287,6 +338,7 @@ void MainModel::OnResults(uint64_t timestamp, size_t sampleIndex, search::Result context.m_initialized = true; } + m_view->ShowNonFoundResults(context.m_nonFoundResults, context.m_nonFoundResultsEdits.GetEntries()); ShowMarks(context); @@ -313,3 +365,33 @@ void MainModel::ShowMarks(Context const & context) m_view->ShowNonFoundResultsMarks(context.m_nonFoundResults, context.m_nonFoundResultsEdits.GetEntries()); } + +template +void MainModel::ForMatchingEntries(Context & context, FeatureID const & id, Fn && fn) +{ + CHECK(context.m_initialized, ()); + + auto const & foundResults = context.m_foundResults; + CHECK_EQUAL(foundResults.GetCount(), context.m_foundResultsEdits.NumEntries(), ()); + for (size_t i = 0; i < foundResults.GetCount(); ++i) + { + auto const & result = foundResults[i]; + if (result.GetResultType() != search::Result::RESULT_FEATURE) + continue; + if (result.GetFeatureID() == id) + fn(context.m_foundResultsEdits, i); + } + + FeatureType ft; + CHECK(m_loader.Load(id, ft), ("Can't load feature:", id)); + search::Matcher matcher(m_loader); + + auto const & nonFoundResults = context.m_nonFoundResults; + CHECK_EQUAL(nonFoundResults.size(), context.m_nonFoundResultsEdits.NumEntries(), ()); + for (size_t i = 0; i < nonFoundResults.size(); ++i) + { + auto const & result = context.m_nonFoundResults[i]; + if (matcher.Matches(result, ft)) + fn(context.m_nonFoundResultsEdits, i); + } +} diff --git a/search/search_quality/assessment_tool/main_model.hpp b/search/search_quality/assessment_tool/main_model.hpp index 462d309489..ceacf682da 100644 --- a/search/search_quality/assessment_tool/main_model.hpp +++ b/search/search_quality/assessment_tool/main_model.hpp @@ -1,6 +1,7 @@ #pragma once #include "search/engine.hpp" +#include "search/feature_loader.hpp" #include "search/search_quality/assessment_tool/context.hpp" #include "search/search_quality/assessment_tool/edits.hpp" #include "search/search_quality/assessment_tool/model.hpp" @@ -37,6 +38,8 @@ public: void OnShowViewportClicked() override; void OnShowPositionClicked() override; bool HasChanges() override; + bool AlreadyInSamples(FeatureID const & id) override; + void AddNonFoundResult(FeatureID const & id) override; private: static int constexpr kInvalidIndex = -1; @@ -51,8 +54,12 @@ private: void ResetSearch(); void ShowMarks(Context const & context); + template + void ForMatchingEntries(Context & context, FeatureID const & id, Fn && fn); + Framework & m_framework; Index const & m_index; + search::FeatureLoader m_loader; ContextList m_contexts; diff --git a/search/search_quality/assessment_tool/main_view.cpp b/search/search_quality/assessment_tool/main_view.cpp index 191ae3e8ae..6c4a6cf28b 100644 --- a/search/search_quality/assessment_tool/main_view.cpp +++ b/search/search_quality/assessment_tool/main_view.cpp @@ -10,6 +10,7 @@ #include "qt/qt_common/scale_slider.hpp" #include "map/framework.hpp" +#include "map/place_page_info.hpp" #include "geometry/mercator.hpp" @@ -43,6 +44,14 @@ MainView::MainView(Framework & framework) : m_framework(framework) InitMapWidget(); InitDocks(); InitMenuBar(); + + m_framework.SetMapSelectionListeners( + [this](place_page::Info const & info) { + auto const & selectedFeature = info.GetID(); + if (selectedFeature.IsValid()) + m_selectedFeature = selectedFeature; + }, + [this](bool /* switchFullScreenMode */) { m_selectedFeature = FeatureID(); }); } MainView::~MainView() @@ -60,9 +69,17 @@ void MainView::SetSamples(ContextList::SamplesSlice const & samples) m_sampleView->Clear(); } -void MainView::OnSearchStarted() { m_sampleView->OnSearchStarted(); } +void MainView::OnSearchStarted() +{ + m_state = State::Search; + m_sampleView->OnSearchStarted(); +} -void MainView::OnSearchCompleted() { m_sampleView->OnSearchCompleted(); } +void MainView::OnSearchCompleted() +{ + m_state = State::AfterSearch; + m_sampleView->OnSearchCompleted(); +} void MainView::ShowSample(size_t sampleIndex, search::Sample const & sample, bool positionAvailable, bool hasEdits) @@ -77,9 +94,9 @@ void MainView::ShowSample(size_t sampleIndex, search::Sample const & sample, boo OnSampleChanged(sampleIndex, hasEdits); } -void MainView::ShowFoundResults(search::Results::ConstIter begin, search::Results::ConstIter end) +void MainView::AddFoundResults(search::Results::ConstIter begin, search::Results::ConstIter end) { - m_sampleView->ShowFoundResults(begin, end); + m_sampleView->AddFoundResults(begin, end); } void MainView::ShowNonFoundResults(std::vector const & results, @@ -266,6 +283,8 @@ void MainView::InitMapWidget() { auto * mapWidget = new qt::common::MapWidget(m_framework, false /* apiOpenGLES3 */, widget /* parent */); + connect(mapWidget, &qt::common::MapWidget::OnContextMenuRequested, + [this](QPoint const & p) { AddSelectedFeature(p); }); auto * toolBar = new QToolBar(widget /* parent */); toolBar->setOrientation(Qt::Vertical); toolBar->setIconSize(QSize(32, 32)); @@ -384,6 +403,26 @@ MainView::SaveResult MainView::TryToSaveEdits(QString const & msg) return SaveResult::Cancelled; } +void MainView::AddSelectedFeature(QPoint const & p) +{ + auto const selectedFeature = m_selectedFeature; + + if (!selectedFeature.IsValid()) + return; + + if (m_state != State::AfterSearch) + return; + + if (m_model->AlreadyInSamples(selectedFeature)) + return; + + QMenu menu; + auto const * action = menu.addAction("Add to non-found results"); + connect(action, &QAction::triggered, + [this, selectedFeature]() { m_model->AddNonFoundResult(selectedFeature); }); + menu.exec(p); +} + QDockWidget * MainView::CreateDock(QWidget & widget) { auto * dock = new QDockWidget(QString(), this /* parent */, Qt::Widget); diff --git a/search/search_quality/assessment_tool/main_view.hpp b/search/search_quality/assessment_tool/main_view.hpp index 82011ff908..03b29c2885 100644 --- a/search/search_quality/assessment_tool/main_view.hpp +++ b/search/search_quality/assessment_tool/main_view.hpp @@ -2,6 +2,8 @@ #include "search/search_quality/assessment_tool/view.hpp" +#include "indexer/feature_decl.hpp" + #include class Framework; @@ -33,7 +35,7 @@ public: void ShowSample(size_t sampleIndex, search::Sample const & sample, bool positionAvailable, bool hasEdits) override; - void ShowFoundResults(search::Results::ConstIter begin, search::Results::ConstIter end) override; + void AddFoundResults(search::Results::ConstIter begin, search::Results::ConstIter end) override; void ShowNonFoundResults(std::vector const & results, std::vector const & entries) override; @@ -67,6 +69,23 @@ private slots: void OnNonFoundResultSelected(QItemSelection const & current); private: + enum class State + { + BeforeSearch, + Search, + AfterSearch + }; + + friend string DebugPrint(State state) + { + switch (state) + { + case State::BeforeSearch: return "BeforeSearch"; + case State::Search: return "Search"; + case State::AfterSearch: return "AfterSearch"; + } + } + enum class SaveResult { NoEdits, @@ -87,6 +106,8 @@ private: void SetSampleDockTitle(bool hasEdits); SaveResult TryToSaveEdits(QString const & msg); + void AddSelectedFeature(QPoint const & p); + QDockWidget * CreateDock(QWidget & widget); Framework & m_framework; @@ -99,4 +120,7 @@ private: QAction * m_save = nullptr; QAction * m_saveAs = nullptr; + + State m_state = State::BeforeSearch; + FeatureID m_selectedFeature; }; diff --git a/search/search_quality/assessment_tool/model.hpp b/search/search_quality/assessment_tool/model.hpp index e4746d46b6..aac829e171 100644 --- a/search/search_quality/assessment_tool/model.hpp +++ b/search/search_quality/assessment_tool/model.hpp @@ -3,6 +3,7 @@ #include class View; +struct FeatureID; class Model { @@ -22,6 +23,9 @@ public: virtual void OnShowPositionClicked() = 0; virtual bool HasChanges() = 0; + virtual bool AlreadyInSamples(FeatureID const & id) = 0; + virtual void AddNonFoundResult(FeatureID const & id) = 0; + protected: View * m_view = nullptr; }; diff --git a/search/search_quality/assessment_tool/result_view.cpp b/search/search_quality/assessment_tool/result_view.cpp index d6da1e95a7..bdff69d2d6 100644 --- a/search/search_quality/assessment_tool/result_view.cpp +++ b/search/search_quality/assessment_tool/result_view.cpp @@ -63,9 +63,9 @@ ResultView::ResultView(search::Sample::Result const & result, QWidget & parent) { } -void ResultView::SetEditor(Edits::RelevanceEditor && editor) +void ResultView::SetEditor(Edits::Editor && editor) { - m_editor = my::make_unique(std::move(editor)); + m_editor = my::make_unique(std::move(editor)); m_irrelevant->setChecked(false); m_relevant->setChecked(false); @@ -83,10 +83,19 @@ void ResultView::SetEditor(Edits::RelevanceEditor && editor) void ResultView::Update() { - if (m_editor && m_editor->HasChanges()) - setStyleSheet("#result {background: rgba(255, 255, 200, 50%)}"); + if (m_editor) + { + if (m_editor->GetOrigin() == Edits::Entry::Origin::Created) + setStyleSheet("#result {background: rgba(173, 223, 173, 50%)}"); + else if (m_editor->HasChanges()) + setStyleSheet("#result {background: rgba(255, 255, 200, 50%)}"); + else + setStyleSheet(""); + } else + { setStyleSheet(""); + } } void ResultView::Init() diff --git a/search/search_quality/assessment_tool/result_view.hpp b/search/search_quality/assessment_tool/result_view.hpp index 0a42c47e16..380a327524 100644 --- a/search/search_quality/assessment_tool/result_view.hpp +++ b/search/search_quality/assessment_tool/result_view.hpp @@ -24,7 +24,7 @@ public: ResultView(search::Result const & result, QWidget & parent); ResultView(search::Sample::Result const & result, QWidget & parent); - void SetEditor(Edits::RelevanceEditor && editor); + void SetEditor(Edits::Editor && editor); void Update(); @@ -47,5 +47,5 @@ private: QRadioButton * m_relevant = nullptr; QRadioButton * m_vital = nullptr; - std::unique_ptr m_editor; + std::unique_ptr m_editor; }; diff --git a/search/search_quality/assessment_tool/results_view.cpp b/search/search_quality/assessment_tool/results_view.cpp index 246bba6dbb..e8323a98b9 100644 --- a/search/search_quality/assessment_tool/results_view.cpp +++ b/search/search_quality/assessment_tool/results_view.cpp @@ -57,6 +57,12 @@ void ResultsView::Update(Edits::Update const & update) result->Update(); break; } + case Edits::Update::Type::Add: + { + CHECK_LESS(update.m_index, m_results.size(), ()); + m_results[update.m_index]->Update(); + break; + } case Edits::Update::Type::Delete: { auto const index = update.m_index; @@ -64,6 +70,11 @@ void ResultsView::Update(Edits::Update const & update) item(static_cast(index))->setHidden(true); break; } + case Edits::Update::Type::Resurrect: + auto const index = update.m_index; + CHECK_LESS(index, Size(), ()); + item(static_cast(index))->setHidden(false); + break; }; } diff --git a/search/search_quality/assessment_tool/sample_view.cpp b/search/search_quality/assessment_tool/sample_view.cpp index 1a4de6f029..a460461f4f 100644 --- a/search/search_quality/assessment_tool/sample_view.cpp +++ b/search/search_quality/assessment_tool/sample_view.cpp @@ -178,7 +178,7 @@ void SampleView::OnSearchStarted() { m_spinner->Show(); } void SampleView::OnSearchCompleted() { m_spinner->Hide(); } -void SampleView::ShowFoundResults(search::Results::ConstIter begin, search::Results::ConstIter end) +void SampleView::AddFoundResults(search::Results::ConstIter begin, search::Results::ConstIter end) { for (auto it = begin; it != end; ++it) m_foundResults->Add(*it /* result */); @@ -194,6 +194,8 @@ void SampleView::ShowNonFoundResults(std::vector const & guard.m_controller.SetIsVisible(true); guard.m_controller.SetIsDrawable(true); + m_nonFoundResults->Clear(); + bool allDeleted = true; for (size_t i = 0; i < results.size(); ++i) { @@ -278,7 +280,7 @@ void SampleView::SetEdits(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).SetEditor(Edits::RelevanceEditor(edits, i)); + results.Get(i).SetEditor(Edits::Editor(edits, i)); } void SampleView::OnRemoveNonFoundResult(int row) { m_nonFoundResultsEdits->Delete(row); } diff --git a/search/search_quality/assessment_tool/sample_view.hpp b/search/search_quality/assessment_tool/sample_view.hpp index 0b8a01f688..aca6e59c77 100644 --- a/search/search_quality/assessment_tool/sample_view.hpp +++ b/search/search_quality/assessment_tool/sample_view.hpp @@ -27,7 +27,7 @@ public: void OnSearchStarted(); void OnSearchCompleted(); - void ShowFoundResults(search::Results::ConstIter begin, search::Results::ConstIter end); + void AddFoundResults(search::Results::ConstIter begin, search::Results::ConstIter end); void ShowNonFoundResults(std::vector const & results, std::vector const & entries); diff --git a/search/search_quality/assessment_tool/view.hpp b/search/search_quality/assessment_tool/view.hpp index 22c64b8296..166418e281 100644 --- a/search/search_quality/assessment_tool/view.hpp +++ b/search/search_quality/assessment_tool/view.hpp @@ -33,8 +33,8 @@ public: virtual void ShowSample(size_t index, search::Sample const & sample, bool positionAvailable, bool hasEdits) = 0; - virtual void ShowFoundResults(search::Results::ConstIter begin, - search::Results::ConstIter end) = 0; + virtual void AddFoundResults(search::Results::ConstIter begin, + search::Results::ConstIter end) = 0; virtual void ShowNonFoundResults(std::vector const & results, std::vector const & entries) = 0; diff --git a/search/search_quality/matcher.cpp b/search/search_quality/matcher.cpp index 12988ae066..7fa0f5d5dd 100644 --- a/search/search_quality/matcher.cpp +++ b/search/search_quality/matcher.cpp @@ -50,17 +50,10 @@ void Matcher::Match(std::vector const & golden, std::vector #include +class FeatureType; + namespace search { class FeatureLoader; @@ -21,9 +23,10 @@ public: void Match(std::vector const & golden, std::vector const & actual, std::vector & goldenMatching, std::vector & actualMatching); -private: + bool Matches(Sample::Result const & golden, FeatureType & ft); bool Matches(Sample::Result const & golden, Result const & actual); +private: FeatureLoader & m_loader; }; } // namespace search