Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mapsme/omim.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/search
diff options
context:
space:
mode:
authorMaxim Pimenov <m@maps.me>2019-04-12 14:54:48 +0300
committerTatiana Yan <tatiana.kondakova@gmail.com>2019-04-15 16:47:25 +0300
commit861bc09eeb39c677c8791983d65246d4ebda3b4c (patch)
tree272860ac4d116f7c28d1a6027d9f31dbc722ec33 /search
parentb4e8f38c7c9b4e4c3dc2c9d413342d29d587889b (diff)
[search] [assessment-tool] Samples can now be marked as useless.
Diffstat (limited to 'search')
-rw-r--r--search/search_quality/assessment_tool/context.cpp21
-rw-r--r--search/search_quality/assessment_tool/context.hpp22
-rw-r--r--search/search_quality/assessment_tool/edits.hpp32
-rw-r--r--search/search_quality/assessment_tool/main_model.cpp43
-rw-r--r--search/search_quality/assessment_tool/main_model.hpp2
-rw-r--r--search/search_quality/assessment_tool/main_view.cpp27
-rw-r--r--search/search_quality/assessment_tool/main_view.hpp7
-rw-r--r--search/search_quality/assessment_tool/model.hpp1
-rw-r--r--search/search_quality/assessment_tool/sample_view.cpp27
-rw-r--r--search/search_quality/assessment_tool/sample_view.hpp4
-rw-r--r--search/search_quality/assessment_tool/samples_view.cpp22
-rw-r--r--search/search_quality/assessment_tool/samples_view.hpp11
-rw-r--r--search/search_quality/assessment_tool/view.hpp5
-rw-r--r--search/search_quality/sample.cpp3
-rw-r--r--search/search_quality/sample.hpp5
15 files changed, 200 insertions, 32 deletions
diff --git a/search/search_quality/assessment_tool/context.cpp b/search/search_quality/assessment_tool/context.cpp
index 8a968b2a4c..15d32d473c 100644
--- a/search/search_quality/assessment_tool/context.cpp
+++ b/search/search_quality/assessment_tool/context.cpp
@@ -21,9 +21,18 @@ void Context::Clear()
m_nonFoundResults.clear();
m_nonFoundResultsEdits.Clear();
+ m_sampleEdits.Clear();
+
m_initialized = false;
}
+void Context::LoadFromSample(search::Sample const & sample)
+{
+ Clear();
+ m_sample = sample;
+ m_sampleEdits.Reset(sample.m_useless);
+}
+
search::Sample Context::MakeSample(search::FeatureLoader & loader) const
{
search::Sample outSample = m_sample;
@@ -93,6 +102,8 @@ search::Sample Context::MakeSample(search::FeatureLoader & loader) const
outResults.push_back(search::Sample::Result::Build(*ft, *foundEntries[i].m_currRelevance));
}
+ outSample.m_useless = m_sampleEdits.m_currUseless;
+
return outSample;
}
@@ -102,12 +113,15 @@ void Context::ApplyEdits()
return;
m_foundResultsEdits.Apply();
m_nonFoundResultsEdits.Apply();
+ m_sampleEdits.Apply();
}
// ContextList -------------------------------------------------------------------------------------
-ContextList::ContextList(OnUpdate onResultsUpdate, OnUpdate onNonFoundResultsUpdate)
+ContextList::ContextList(OnUpdate onResultsUpdate, OnUpdate onNonFoundResultsUpdate,
+ OnSampleUpdate onSampleUpdate)
: m_onResultsUpdate(onResultsUpdate)
, m_onNonFoundResultsUpdate(onNonFoundResultsUpdate)
+ , m_onSampleUpdate(onSampleUpdate)
{
}
@@ -133,6 +147,11 @@ void ContextList::Resize(size_t size)
OnContextUpdated(i);
if (m_onNonFoundResultsUpdate)
m_onNonFoundResultsUpdate(i, update);
+ },
+ [this, i]() {
+ OnContextUpdated(i);
+ if (m_onSampleUpdate)
+ m_onSampleUpdate(i);
});
}
}
diff --git a/search/search_quality/assessment_tool/context.hpp b/search/search_quality/assessment_tool/context.hpp
index 35d111771d..1b86975958 100644
--- a/search/search_quality/assessment_tool/context.hpp
+++ b/search/search_quality/assessment_tool/context.hpp
@@ -26,8 +26,11 @@ struct Context
Completed
};
- Context(Edits::OnUpdate onFoundResultsUpdate, Edits::OnUpdate onNonFoundResultsUpdate)
- : m_foundResultsEdits(onFoundResultsUpdate), m_nonFoundResultsEdits(onNonFoundResultsUpdate)
+ Context(Edits::OnUpdate onFoundResultsUpdate, Edits::OnUpdate onNonFoundResultsUpdate,
+ SampleEdits::OnUpdate onSampleUpdate)
+ : m_foundResultsEdits(onFoundResultsUpdate)
+ , m_nonFoundResultsEdits(onNonFoundResultsUpdate)
+ , m_sampleEdits(onSampleUpdate)
{
}
@@ -42,8 +45,12 @@ struct Context
m_nonFoundResultsEdits.Add(result.m_relevance);
}
+ bool IsUseless() const { return m_sampleEdits.m_currUseless; }
+
bool HasChanges() const
{
+ if (m_sampleEdits.HasChanges())
+ return true;
if (!m_initialized)
return false;
return m_foundResultsEdits.HasChanges() || m_nonFoundResultsEdits.HasChanges();
@@ -51,6 +58,8 @@ struct Context
void Clear();
+ void LoadFromSample(search::Sample const & sample);
+
// Makes sample in accordance with uncommited edits.
search::Sample MakeSample(search::FeatureLoader & loader) const;
@@ -67,6 +76,8 @@ struct Context
std::vector<search::Sample::Result> m_nonFoundResults;
Edits m_nonFoundResultsEdits;
+ SampleEdits m_sampleEdits;
+
SearchState m_searchState = SearchState::Untouched;
bool m_initialized = false;
@@ -95,6 +106,8 @@ public:
return (*m_contexts)[index].m_searchState;
}
+ bool IsUseless(size_t index) const { return (*m_contexts)[index].m_sampleEdits.m_currUseless; }
+
size_t Size() const { return m_contexts->Size(); }
private:
@@ -102,8 +115,10 @@ public:
};
using OnUpdate = std::function<void(size_t index, Edits::Update const & update)>;
+ using OnSampleUpdate = std::function<void(size_t index)>;
- ContextList(OnUpdate onResultsUpdate, OnUpdate onNonFoundResultsUpdate);
+ ContextList(OnUpdate onResultsUpdate, OnUpdate onNonFoundResultsUpdate,
+ OnSampleUpdate onSampleUpdate);
void Resize(size_t size);
size_t Size() const { return m_contexts.size(); }
@@ -128,4 +143,5 @@ private:
OnUpdate m_onResultsUpdate;
OnUpdate m_onNonFoundResultsUpdate;
+ OnSampleUpdate m_onSampleUpdate;
};
diff --git a/search/search_quality/assessment_tool/edits.hpp b/search/search_quality/assessment_tool/edits.hpp
index 3e782f7d98..34e2508a6a 100644
--- a/search/search_quality/assessment_tool/edits.hpp
+++ b/search/search_quality/assessment_tool/edits.hpp
@@ -12,6 +12,38 @@
#include <boost/optional.hpp>
+struct SampleEdits
+{
+ using OnUpdate = std::function<void()>;
+
+ SampleEdits(OnUpdate onUpdate) : m_onUpdate(onUpdate) {}
+
+ void Reset(bool origUseless)
+ {
+ m_origUseless = origUseless;
+ m_currUseless = origUseless;
+ }
+
+ void FlipUsefulness()
+ {
+ m_currUseless ^= true;
+ if (m_onUpdate)
+ m_onUpdate();
+ }
+
+ void Apply() { m_origUseless = m_currUseless; }
+
+ bool HasChanges() const { return m_origUseless != m_currUseless; }
+
+ void Clear() {}
+
+ bool m_origUseless = false;
+ bool m_currUseless = false;
+
+ OnUpdate m_onUpdate;
+};
+
+// todo(@m) Rename to ResultsEdits?
class Edits
{
public:
diff --git a/search/search_quality/assessment_tool/main_model.cpp b/search/search_quality/assessment_tool/main_model.cpp
index 802e757d5c..a22e82cf7e 100644
--- a/search/search_quality/assessment_tool/main_model.cpp
+++ b/search/search_quality/assessment_tool/main_model.cpp
@@ -36,12 +36,13 @@ MainModel::MainModel(Framework & framework)
},
[this](size_t sampleIndex, Edits::Update const & update) {
OnUpdate(View::ResultType::NonFound, sampleIndex, update);
- })
+ },
+ [this](size_t sampleIndex) { OnSampleUpdate(sampleIndex); })
, m_runner(m_framework, m_dataSource, m_contexts,
[this](search::Results const & results) { UpdateViewOnResults(results); },
[this](size_t index) {
- // The second parameter does not matter, we only change SearchStatus.
- m_view->OnSampleChanged(index, false /* hasEdits */);
+ // Only the first parameter matters because we only change SearchStatus.
+ m_view->OnSampleChanged(index, false /* isUseless */, false /* hasEdits */);
})
{
search::CheckLocale();
@@ -77,11 +78,8 @@ void MainModel::Open(string const & path)
m_contexts.Resize(samples.size());
for (size_t i = 0; i < samples.size(); ++i)
- {
- auto & context = m_contexts[i];
- context.Clear();
- context.m_sample = samples[i];
- }
+ m_contexts[i].LoadFromSample(samples[i]);
+
m_path = path;
m_view->SetSamples(ContextList::SamplesSlice(m_contexts));
@@ -134,7 +132,7 @@ void MainModel::OnSampleSelected(int index)
auto & context = m_contexts[index];
auto const & sample = context.m_sample;
- m_view->ShowSample(index, sample, sample.m_pos, context.HasChanges());
+ m_view->ShowSample(index, sample, sample.m_pos, context.IsUseless(), context.HasChanges());
m_runner.ResetForegroundSearch();
m_numShownResults = 0;
@@ -271,12 +269,22 @@ void MainModel::AddNonFoundResult(FeatureID const & id)
context.AddNonFoundResult(result);
}
+void MainModel::FlipSampleUsefulness(int index)
+{
+ CHECK_EQUAL(m_selectedSample, index, ());
+
+ m_contexts[index].m_sampleEdits.FlipUsefulness();
+
+ // Don't bother with resetting search: we cannot tell whether
+ // the sample is useless without its results anyway.
+}
+
void MainModel::InitiateForegroundSearch(size_t index)
{
auto & context = m_contexts[index];
auto const & sample = context.m_sample;
- m_view->ShowSample(index, sample, sample.m_pos, context.HasChanges());
+ m_view->ShowSample(index, sample, sample.m_pos, context.IsUseless(), context.HasChanges());
m_runner.InitiateForegroundSearch(index);
m_view->OnSearchStarted();
}
@@ -299,7 +307,7 @@ void MainModel::OnUpdate(View::ResultType type, size_t sampleIndex, Edits::Updat
}
m_view->OnResultChanged(sampleIndex, type, update);
- m_view->OnSampleChanged(sampleIndex, context.HasChanges());
+ m_view->OnSampleChanged(sampleIndex, context.IsUseless(), context.HasChanges());
m_view->OnSamplesChanged(m_contexts.HasChanges());
if (update.m_type == Type::Add || update.m_type == Type::Resurrect ||
@@ -311,6 +319,14 @@ void MainModel::OnUpdate(View::ResultType type, size_t sampleIndex, Edits::Updat
}
}
+void MainModel::OnSampleUpdate(size_t sampleIndex)
+{
+ auto & context = m_contexts[sampleIndex];
+
+ m_view->OnSampleChanged(sampleIndex, context.IsUseless(), context.HasChanges());
+ m_view->OnSamplesChanged(m_contexts.HasChanges());
+}
+
void MainModel::UpdateViewOnResults(search::Results const & results)
{
CHECK(m_threadChecker.CalledOnOriginalThread(), ());
@@ -329,7 +345,8 @@ void MainModel::UpdateViewOnResults(search::Results const & results)
m_view->ShowMarks(context);
m_view->OnResultChanged(m_selectedSample, View::ResultType::Found, Edits::Update::MakeAll());
m_view->OnResultChanged(m_selectedSample, View::ResultType::NonFound, Edits::Update::MakeAll());
- m_view->OnSampleChanged(m_selectedSample, context.HasChanges());
+ m_view->OnSampleChanged(m_selectedSample, context.IsUseless(), context.HasChanges());
+ m_view->OnSamplesChanged(m_contexts.HasChanges());
m_view->SetEdits(m_selectedSample, context.m_foundResultsEdits, context.m_nonFoundResultsEdits);
m_view->OnSearchCompleted();
@@ -346,7 +363,7 @@ void MainModel::OnChangeAllRelevancesClicked(Edits::Relevance relevance)
m_view->OnResultChanged(m_selectedSample, View::ResultType::Found, Edits::Update::MakeAll());
m_view->OnResultChanged(m_selectedSample, View::ResultType::NonFound, Edits::Update::MakeAll());
- m_view->OnSampleChanged(m_selectedSample, context.HasChanges());
+ m_view->OnSampleChanged(m_selectedSample, context.IsUseless(), context.HasChanges());
m_view->OnSamplesChanged(m_contexts.HasChanges());
}
diff --git a/search/search_quality/assessment_tool/main_model.hpp b/search/search_quality/assessment_tool/main_model.hpp
index 78079c4b35..4f5ba0eff7 100644
--- a/search/search_quality/assessment_tool/main_model.hpp
+++ b/search/search_quality/assessment_tool/main_model.hpp
@@ -47,6 +47,7 @@ public:
bool HasChanges() override;
bool AlreadyInSamples(FeatureID const & id) override;
void AddNonFoundResult(FeatureID const & id) override;
+ void FlipSampleUsefulness(int index) override;
private:
static int constexpr kInvalidIndex = -1;
@@ -54,6 +55,7 @@ private:
void InitiateForegroundSearch(size_t index);
void OnUpdate(View::ResultType type, size_t sampleIndex, Edits::Update const & update);
+ void OnSampleUpdate(size_t sampleIndex);
void UpdateViewOnResults(search::Results const & results);
void ShowMarks(Context const & context);
diff --git a/search/search_quality/assessment_tool/main_view.cpp b/search/search_quality/assessment_tool/main_view.cpp
index a2b10c393c..838693d8cc 100644
--- a/search/search_quality/assessment_tool/main_view.cpp
+++ b/search/search_quality/assessment_tool/main_view.cpp
@@ -110,7 +110,8 @@ void MainView::OnSearchCompleted()
}
void MainView::ShowSample(size_t sampleIndex, search::Sample const & sample,
- boost::optional<m2::PointD> const & position, bool hasEdits)
+ boost::optional<m2::PointD> const & position, bool isUseless,
+ bool hasEdits)
{
m_sampleLocale = sample.m_locale;
@@ -121,7 +122,7 @@ void MainView::ShowSample(size_t sampleIndex, search::Sample const & sample,
OnResultChanged(sampleIndex, ResultType::Found, Edits::Update::MakeAll());
OnResultChanged(sampleIndex, ResultType::NonFound, Edits::Update::MakeAll());
- OnSampleChanged(sampleIndex, hasEdits);
+ OnSampleChanged(sampleIndex, isUseless, hasEdits);
}
void MainView::AddFoundResults(search::Results::ConstIter begin, search::Results::ConstIter end)
@@ -190,12 +191,13 @@ void MainView::OnResultChanged(size_t sampleIndex, ResultType type, Edits::Updat
}
}
-void MainView::OnSampleChanged(size_t sampleIndex, bool hasEdits)
+void MainView::OnSampleChanged(size_t sampleIndex, bool isUseless, bool hasEdits)
{
m_samplesView->OnUpdate(sampleIndex);
if (!m_samplesView->IsSelected(sampleIndex))
return;
- SetSampleDockTitle(hasEdits);
+ SetSampleDockTitle(isUseless, hasEdits);
+ m_sampleView->OnUselessnessChanged(isUseless);
}
void MainView::OnSamplesChanged(bool hasEdits)
@@ -225,7 +227,7 @@ void MainView::Clear()
SetSamplesDockTitle(false /* hasEdits */);
m_sampleView->Clear();
- SetSampleDockTitle(false /* hasEdits */);
+ SetSampleDockTitle(false /* isUseless */, false /* hasEdits */);
m_skipFeatureInfoDialog = false;
m_sampleLocale.clear();
@@ -361,6 +363,9 @@ void MainView::InitDocks()
connect(model, &QItemSelectionModel::selectionChanged, this, &MainView::OnSampleSelected);
}
+ connect(m_samplesView, &SamplesView::FlipSampleUsefulness,
+ [this](int index) { m_model->FlipSampleUsefulness(index); });
+
m_samplesDock = CreateDock(*m_samplesView);
addDockWidget(Qt::LeftDockWidgetArea, m_samplesDock);
SetSamplesDockTitle(false /* hasEdits */);
@@ -393,7 +398,7 @@ void MainView::InitDocks()
connect(m_sampleDock, &QDockWidget::dockLocationChanged,
[this](Qt::DockWidgetArea area) { m_sampleView->OnLocationChanged(area); });
addDockWidget(Qt::RightDockWidgetArea, m_sampleDock);
- SetSampleDockTitle(false /* hasEdits */);
+ SetSampleDockTitle(false /* isUseless */, false /* hasEdits */);
}
void MainView::Open()
@@ -479,13 +484,15 @@ void MainView::SetSamplesDockTitle(bool hasEdits)
m_samplesDock->setWindowTitle(tr("Samples"));
}
-void MainView::SetSampleDockTitle(bool hasEdits)
+void MainView::SetSampleDockTitle(bool isUseless, bool hasEdits)
{
CHECK(m_sampleDock, ());
+ std::string title = "Sample";
if (hasEdits)
- m_sampleDock->setWindowTitle(tr("Sample *"));
- else
- m_sampleDock->setWindowTitle(tr("Sample"));
+ title += " *";
+ if (isUseless)
+ title += " (useless)";
+ m_sampleDock->setWindowTitle(tr(title.data()));
}
MainView::SaveResult MainView::TryToSaveEdits(QString const & msg)
diff --git a/search/search_quality/assessment_tool/main_view.hpp b/search/search_quality/assessment_tool/main_view.hpp
index 6ec9491162..84d4da55da 100644
--- a/search/search_quality/assessment_tool/main_view.hpp
+++ b/search/search_quality/assessment_tool/main_view.hpp
@@ -33,7 +33,8 @@ public:
void OnSearchStarted() override;
void OnSearchCompleted() override;
void ShowSample(size_t sampleIndex, search::Sample const & sample,
- boost::optional<m2::PointD> const & position, bool hasEdits) override;
+ boost::optional<m2::PointD> const & position, bool isUseless,
+ bool hasEdits) override;
void AddFoundResults(search::Results::ConstIter begin, search::Results::ConstIter end) override;
void ShowNonFoundResults(std::vector<search::Sample::Result> const & results,
@@ -53,7 +54,7 @@ public:
void OnResultChanged(size_t sampleIndex, ResultType type, Edits::Update const & update) override;
void SetEdits(size_t sampleIndex, Edits & foundResultsEdits,
Edits & nonFoundResultsEdits) override;
- void OnSampleChanged(size_t sampleIndex, bool hasEdits) override;
+ void OnSampleChanged(size_t sampleIndex, bool isUseless, bool hasEdits) override;
void OnSamplesChanged(bool hasEdits) override;
void ShowError(std::string const & msg) override;
@@ -105,7 +106,7 @@ private:
void InitiateBackgroundSearch();
void SetSamplesDockTitle(bool hasEdits);
- void SetSampleDockTitle(bool hasEdits);
+ void SetSampleDockTitle(bool isUseless, bool hasEdits);
SaveResult TryToSaveEdits(QString const & msg);
void AddSelectedFeature(QPoint const & p);
diff --git a/search/search_quality/assessment_tool/model.hpp b/search/search_quality/assessment_tool/model.hpp
index 3b98b75dd0..fe9b3ea98d 100644
--- a/search/search_quality/assessment_tool/model.hpp
+++ b/search/search_quality/assessment_tool/model.hpp
@@ -35,6 +35,7 @@ public:
virtual bool AlreadyInSamples(FeatureID const & id) = 0;
virtual void AddNonFoundResult(FeatureID const & id) = 0;
+ virtual void FlipSampleUsefulness(int index) = 0;
protected:
View * m_view = nullptr;
diff --git a/search/search_quality/assessment_tool/sample_view.cpp b/search/search_quality/assessment_tool/sample_view.cpp
index 969de74900..cd702c7230 100644
--- a/search/search_quality/assessment_tool/sample_view.cpp
+++ b/search/search_quality/assessment_tool/sample_view.cpp
@@ -101,6 +101,7 @@ SampleView::SampleView(QWidget * parent, Framework & framework)
auto * layout =
BuildSubLayout<QVBoxLayout>(*mainLayout, *this /* parent */, &m_relatedQueriesBox);
SetVerticalStretch(*m_relatedQueriesBox, 1 /* stretch */);
+
layout->addWidget(new QLabel(tr("Related queries")));
m_relatedQueries = new QListWidget();
@@ -122,6 +123,13 @@ SampleView::SampleView(QWidget * parent, Framework & framework)
}
{
+ m_uselessnessLabel = new QLabel(this /* parent */);
+ m_uselessnessLabel->setText(tr("Sample is marked as useless"));
+ m_uselessnessLabel->hide();
+ mainLayout->addWidget(m_uselessnessLabel);
+ }
+
+ {
auto * layout =
BuildSubLayout<QVBoxLayout>(*mainLayout, *this /* parent */, &m_foundResultsBox);
SetVerticalStretch(*m_foundResultsBox, 4 /* stretch */);
@@ -305,6 +313,25 @@ void SampleView::SetEdits(Edits & resultsEdits, Edits & nonFoundResultsEdits)
m_nonFoundResultsEdits = &nonFoundResultsEdits;
}
+void SampleView::OnUselessnessChanged(bool isUseless)
+{
+ if (isUseless)
+ {
+ m_uselessnessLabel->show();
+ m_foundResultsBox->hide();
+ m_nonFoundResultsBox->hide();
+ m_markAllAsRelevant->hide();
+ m_markAllAsIrrelevant->hide();
+ }
+ else
+ {
+ m_uselessnessLabel->hide();
+ m_foundResultsBox->show();
+ m_markAllAsRelevant->show();
+ m_markAllAsIrrelevant->show();
+ }
+}
+
void SampleView::Clear()
{
m_query->hide();
diff --git a/search/search_quality/assessment_tool/sample_view.hpp b/search/search_quality/assessment_tool/sample_view.hpp
index 6fc6c9eba7..c8e3578c00 100644
--- a/search/search_quality/assessment_tool/sample_view.hpp
+++ b/search/search_quality/assessment_tool/sample_view.hpp
@@ -44,6 +44,8 @@ public:
void SetEdits(Edits & resultsEdits, Edits & nonFoundResultsEdits);
+ void OnUselessnessChanged(bool isUseless);
+
void Clear();
ResultsView & GetFoundResultsView() { return *m_foundResults; }
@@ -81,6 +83,8 @@ private:
QPushButton * m_markAllAsRelevant = nullptr;
QPushButton * m_markAllAsIrrelevant = nullptr;
+ QLabel * m_uselessnessLabel = nullptr;
+
ResultsView * m_foundResults = nullptr;
QWidget * m_foundResultsBox = nullptr;
diff --git a/search/search_quality/assessment_tool/samples_view.cpp b/search/search_quality/assessment_tool/samples_view.cpp
index 26d8719f37..79376b2226 100644
--- a/search/search_quality/assessment_tool/samples_view.cpp
+++ b/search/search_quality/assessment_tool/samples_view.cpp
@@ -3,9 +3,15 @@
#include "search/search_quality/assessment_tool/helpers.hpp"
#include "base/assert.hpp"
+#include "base/logging.hpp"
+#include <string>
+
+#include <QtGui/QContextMenuEvent>
#include <QtGui/QStandardItem>
+#include <QtWidgets/QAction>
#include <QtWidgets/QHeaderView>
+#include <QtWidgets/QMenu>
// SamplesView::Model ------------------------------------------------------------------------------
SamplesView::Model::Model(QWidget * parent)
@@ -55,3 +61,19 @@ bool SamplesView::IsSelected(size_t index) const
{
return selectionModel()->isRowSelected(base::checked_cast<int>(index), QModelIndex());
}
+
+void SamplesView::contextMenuEvent(QContextMenuEvent * event)
+{
+ QModelIndex modelIndex = selectionModel()->currentIndex();
+ if (!modelIndex.isValid())
+ return;
+
+ int const index = modelIndex.row();
+ bool const isUseless = m_model->SampleIsUseless(index);
+
+ QMenu menu(this);
+ auto const text = std::string(isUseless ? "unmark" : "mark") + " sample as useless";
+ auto const * action = menu.addAction(text.c_str());
+ connect(action, &QAction::triggered, [this, index]() { emit FlipSampleUsefulness(index); });
+ menu.exec(event->globalPos());
+}
diff --git a/search/search_quality/assessment_tool/samples_view.hpp b/search/search_quality/assessment_tool/samples_view.hpp
index f656234619..b8dcea1ccf 100644
--- a/search/search_quality/assessment_tool/samples_view.hpp
+++ b/search/search_quality/assessment_tool/samples_view.hpp
@@ -8,10 +8,13 @@
#include <vector>
#include <QtGui/QStandardItemModel>
+#include <QtWidgets/QMainWindow>
#include <QtWidgets/QTableView>
class SamplesView : public QTableView
{
+ Q_OBJECT
+
public:
explicit SamplesView(QWidget * parent);
@@ -20,6 +23,12 @@ public:
void OnUpdate(size_t index) { m_model->OnUpdate(index); }
void Clear() { m_model->SetSamples(ContextList::SamplesSlice{}); }
+ // QMainWindow overrides:
+ void contextMenuEvent(QContextMenuEvent * event) override;
+
+signals:
+ void FlipSampleUsefulness(int index);
+
private:
class Model : public QStandardItemModel
{
@@ -42,6 +51,8 @@ private:
emit dataChanged(ix, ix);
}
+ bool SampleIsUseless(int index) const { return m_samples.IsUseless(index); }
+
// QStandardItemModel overrides:
QVariant data(QModelIndex const & index, int role = Qt::DisplayRole) const override;
diff --git a/search/search_quality/assessment_tool/view.hpp b/search/search_quality/assessment_tool/view.hpp
index 6abf635ca3..153c3cb00d 100644
--- a/search/search_quality/assessment_tool/view.hpp
+++ b/search/search_quality/assessment_tool/view.hpp
@@ -34,7 +34,8 @@ public:
virtual void OnSearchStarted() = 0;
virtual void OnSearchCompleted() = 0;
virtual void ShowSample(size_t index, search::Sample const & sample,
- boost::optional<m2::PointD> const & position, bool hasEdits) = 0;
+ boost::optional<m2::PointD> const & position, bool isUseless,
+ bool hasEdits) = 0;
virtual void AddFoundResults(search::Results::ConstIter begin,
search::Results::ConstIter end) = 0;
@@ -55,7 +56,7 @@ public:
virtual void OnResultChanged(size_t sampleIndex, ResultType type,
Edits::Update const & update) = 0;
virtual void SetEdits(size_t index, Edits & foundResultsEdits, Edits & nonFoundResultsEdits) = 0;
- virtual void OnSampleChanged(size_t sampleIndex, bool hasEdits) = 0;
+ virtual void OnSampleChanged(size_t sampleIndex, bool isUseless, bool hasEdits) = 0;
virtual void OnSamplesChanged(bool hasEdits) = 0;
virtual void ShowError(std::string const & msg) = 0;
diff --git a/search/search_quality/sample.cpp b/search/search_quality/sample.cpp
index 18ca3c5880..59299d839f 100644
--- a/search/search_quality/sample.cpp
+++ b/search/search_quality/sample.cpp
@@ -168,6 +168,7 @@ void Sample::DeserializeFromJSONImpl(json_t * root)
FromJSONObject(root, "viewport", m_viewport);
FromJSONObjectOptional(root, "results", m_results);
FromJSONObjectOptional(root, "related_queries", m_relatedQueries);
+ FromJSONObjectOptionalField(root, "useless", m_useless);
}
void Sample::SerializeToJSONImpl(json_t & root) const
@@ -178,6 +179,8 @@ void Sample::SerializeToJSONImpl(json_t & root) const
ToJSONObject(root, "viewport", m_viewport);
ToJSONObject(root, "results", m_results);
ToJSONObject(root, "related_queries", m_relatedQueries);
+ if (m_useless)
+ ToJSONObject(root, "useless", m_useless);
}
void Sample::FillSearchParams(search::SearchParams & params) const
diff --git a/search/search_quality/sample.hpp b/search/search_quality/sample.hpp
index e787ccab1e..572ff87ac1 100644
--- a/search/search_quality/sample.hpp
+++ b/search/search_quality/sample.hpp
@@ -76,6 +76,11 @@ struct Sample
m2::RectD m_viewport = m2::RectD(0, 0, 0, 0);
std::vector<Result> m_results;
std::vector<strings::UniString> m_relatedQueries;
+
+ // A useless sample is usually a result of the user exploring
+ // the search engine without a clear search intent or a sample
+ // that cannot be assessed properly using only the data it contains.
+ bool m_useless = false;
};
void FromJSONObject(json_t * root, char const * field,