Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions apps/questions/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,10 @@ def is_contributor(self, user):

return False

@property
def is_solved(self):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this needed because self.solution can be out of date?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added it because there are times you care that a solution exists, but not necessarily what it is: exists() is faster than pulling the whole thing (unless you've already got the solution by select_related).

return Answer.objects.filter(pk=self.solution_id).exists()


class QuestionMetaData(ModelBase):
"""Metadata associated with a support question."""
Expand Down
54 changes: 53 additions & 1 deletion apps/questions/templates/questions/new_question.html
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,59 @@ <h2>{{ _('Summarize your question in a sentence:') }}</h2>
{% endif %}

{% if tried_search and not form %}
{{ show_results(search_results, current_product, current_category, request) }}
{% if results %}
<h2>{{ _("We've found some articles and previous answered questions that may solve your issue:") }}</h2>
{% set button_text = _('None of these solve my problem') %}
<div class="search-results">
{% for result in results %}
<div class="result {{ result.type }}">
<a class="title" href="{{ result.object.get_absolute_url() }}">
{{ result.object.title }}
</a>
{% if result.type == 'document' %}
<p><a href="{{ result.object.get_absolute_url() }}">
{{ result.object.current_revision.summary }}
</a></p>
{% elif result.type == 'question' %}
<p><a href="{{ result.object.get_absolute_url() }}">
{{ result.object.content|truncate(500) }}
</a></p>
<div class="thread-meta">
{% if result.object.is_solved %}
<span class="solved">{{ _('Solved') }}</span>
{% endif %}
<span class="replies">
{% if result.object.num_answers > 0 %}
{{ ngettext('1 reply', '{n} replies',
result.object.num_answers)|f(n=result.object.num_answers) }}
{% else %}
{{ _('No replies') }}
{% endif %}
</span>
<span class="votes">
{{ ngettext('1 person has this problem', '{n} people have this problem',
result.object.num_votes)|f(n=result.object.num_votes) }}
</span>
<span class="votes this-week">
{{ ngettext('1 new this week', '{n} new this week',
result.object.num_votes_past_week)|f(n=result.object.num_votes_past_week) }}
</span>
</div>{# .thread-meta #}
{% endif %}
</div>{# .result #}
{% endfor %}
</div>{# .search-results #}
{% else %}{# No search results at all. #}
<h2 class="no-results">{{ _('This question has not been asked before.') }}</h2>
{% set button_text = _('Provide more details') %}
{% endif %}
<form action="" method="get">
<input type="hidden" name="product" value="{{ current_product.key }}" />
<input type="hidden" name="category" value="{{ current_category.key }}" />
<input type="hidden" name="search" value="{{ request.GET.search }}" />
<input type="hidden" name="showform" value="1" />
<input type="submit" id="show-form-btn" class="btn" value="{{ button_text }}" />
</form>
{% endif %}

{% endblock %}
Expand Down
2 changes: 1 addition & 1 deletion apps/questions/templates/questions/questions.html
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ <h2><a href="{{ question.get_absolute_url() }}">{{ question.title }}</a></h2>
{% include 'questions/includes/have_problem.html' %}
</div>
<div class="thread-meta">
{% if question.solution %}
{% if question.is_solved %}
<span class="solved">{{ _('Solved') }}</span>
{% endif %}
<span><a class="replies" href="{{ question.get_absolute_url()|urlparams(hash='answers') }}">
Expand Down
8 changes: 8 additions & 0 deletions apps/questions/tests/test_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -283,6 +283,14 @@ def test_no_notification_on_update(self):
q.save()
assert not QuestionReplyEvent.is_notifying(q.creator, q)

def test_is_solved_property(self):
a = Answer.objects.all()[0]
q = a.question
assert not q.is_solved
q.solution = a
q.save()
assert q.is_solved


class AddExistingTagTests(TaggingTestCaseBase):
"""Tests for the add_existing_tag helper function."""
Expand Down
87 changes: 40 additions & 47 deletions apps/questions/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -174,14 +174,14 @@ def new_question(request, template=None):
search = request.GET.get('search', '')
if search:
try:
search_results = _search_suggestions(
search, locale_or_default(request.locale))
results = _search_suggestions(
search, locale_or_default(request.locale))
except SearchError:
# Just quietly advance the user to the next step.
search_results = []
results = []
tried_search = True
else:
search_results = []
results = []
tried_search = False

if request.GET.get('showform'):
Expand All @@ -201,7 +201,8 @@ def new_question(request, template=None):
form = None

return jingo.render(request, template,
{'form': form, 'search_results': search_results,
{'form': form,
'results': results,
'tried_search': tried_search,
'products': products,
'current_product': product,
Expand Down Expand Up @@ -772,34 +773,21 @@ def _search_suggestions(query, locale):
query -- full text to search on
locale -- locale to limit to

Items returned are dicts:
{ 'url': URL where the article can be viewed,
'title': Title of the article,
'excerpt_html': Excerpt of the article with search terms hilighted,
formatted in HTML }
Items are dicts of:
{
'type':
'object':
}

Weights wiki pages infinitely higher than questions at the moment.
Returns up to 3 wiki pages, then up to 3 questions.

TODO: ZOMFG this needs to be refactored and the search app should
provide an internal API. Seriously.

"""
def prepare(result, model, attr, searcher, result_to_id):
"""Turn a search result from a Sphinx client into a dict for templates.

Return {} if an object corresponding to the result cannot be found.

"""
try:
obj = model.objects.get(pk=result_to_id(result))
except ObjectDoesNotExist:
return {}
return {'url': obj.get_absolute_url(),
'title': obj.title,
'excerpt_html': searcher.excerpt(getattr(obj, attr), query)}

max_suggestions = settings.QUESTIONS_MAX_SUGGESTIONS
query_limit = max_suggestions + settings.QUESTIONS_SUGGESTION_SLOP
# Max number of search results per type.
WIKI_RESULTS = QUESTIONS_RESULTS = 3

# Search wiki pages:
wiki_searcher = WikiClient()
Expand All @@ -813,28 +801,33 @@ def prepare(result, model, attr, searcher, result_to_id):
'value': [-x for x in settings.SEARCH_DEFAULT_CATEGORIES
if x < 0]}]
raw_results = wiki_searcher.query(query, filters=filters,
limit=query_limit)
limit=WIKI_RESULTS)
# Lazily build excerpts from results. Stop when we have enough:
results = islice((p for p in
(prepare(r, Document, 'html', wiki_searcher,
lambda x: x['id'])
for r in raw_results) if p),
max_suggestions)
results = list(results)

# If we didn't find enough wiki pages to fill the page, pad it out with
# other questions:
if len(results) < max_suggestions:
question_searcher = QuestionsClient()
# questions app is en-US only.
raw_results = question_searcher.query(query,
limit=query_limit - len(results))
results.extend(islice((p for p in
(prepare(r, Question, 'content',
question_searcher,
lambda x: x['attrs']['question_id'])
for r in raw_results) if p),
max_suggestions - len(results)))
results = []
for r in raw_results:
try:
doc = Document.objects.select_related('current_revision').\
get(pk=r['id'])
results.append({
'type': 'document',
'object': doc,
})
except Document.DoesNotExist:
pass

question_searcher = QuestionsClient()
# questions app is en-US only.
raw_results = question_searcher.query(query,
limit=QUESTIONS_RESULTS)
for r in raw_results:
try:
q = Question.objects.get(pk=r['attrs']['question_id'])
results.append({
'type': 'question',
'object': q
})
except Question.DoesNotExist:
pass

return results

Expand Down
13 changes: 13 additions & 0 deletions media/css/questions.css
Original file line number Diff line number Diff line change
Expand Up @@ -1551,3 +1551,16 @@ a#activate-watch, a#remove-watch {
display: block;
font-size: 120%;
}

/* AAQ search result tweaks */
.search-results {
margin-top: 10px;
}

.result .thread-meta {
padding: 10px 15px 10px 50px;
}

.thread-meta .this-week {
border-right: none;
}
Loading