[bug 765816] Move Army of Awesome common replies to KB.

* Replies are now defined in KB article with slug
  'army-of-awesome-common-replies'.
This commit is contained in:
Ricky Rosario 2012-06-20 14:51:42 -04:00
Родитель 0a2cf4223a
Коммит ced4a3141a
4 изменённых файлов: 221 добавлений и 151 удалений

Просмотреть файл

@ -0,0 +1,113 @@
from django.conf import settings
from pyquery import PyQuery
from wiki.models import Document
REPLIES_DOCUMENT_SLUG = 'army-of-awesome-common-replies'
def get_common_replies(locale=settings.WIKI_DEFAULT_LANGUAGE):
"""Returns the common replies.
Parses the KB article with the replies puts them in a list of dicts.
The KB article should have the following wiki syntax structure::
=Category 1=
==Reply 1==
Reply goes here http://example.com/kb-article
==Reply 2==
Another reply here
=Category 2=
==Reply 3==
And another reply
Which results in the following HTML::
<h1 id="w_category-1">Category 1</h1>
<h2 id="w_snippet-1">Reply 1</h2>
<p>Reply goes here <a href="http://example.com/kb-article">
http://example.com/kb-article</a>
</p>
<h2 id="w_snippet-2">Reply 2</h2>
<p>Another reply here
</p>
<h1 id="w_category-2">Category 2</h1>
<h2 id="w_snippet-3">Reply 3</h2>
<p>And another reply
</p>
The resulting list returned would be::
[{'title': 'Category 1',
'responses':
[{'title': 'Reply 1',
'response': 'Reply goes here http://example.com/kb-article'},
{'title': 'Reply 2',
'response': 'Another reply here'}]
},
{'title': 'Category 2',
'responses':
[{'title': 'Reply 3',
'response': 'And another reply'}]
}]
"""
replies = []
# Get the replies document in the right locale, if available.
try:
default_doc = Document.objects.get(
slug=REPLIES_DOCUMENT_SLUG,
locale=settings.WIKI_DEFAULT_LANGUAGE)
except Document.DoesNotExist:
return replies
if locale != default_doc.locale:
translated_doc = default_doc.translated_to(locale)
doc = translated_doc or default_doc
else:
doc = default_doc
# Parse the document HTML into responses.
pq = PyQuery(doc.html)
# Start at the first h1 and traverse down from there.
try:
current_node = pq('h1')[0]
except IndexError:
return replies
current_category = None
current_response = None
while current_node is not None:
if current_node.tag == 'h1':
# New category.
current_category = {
'title': current_node.text,
'responses': []}
replies.append(current_category)
elif current_node.tag == 'h2':
# New response.
current_response = {
'title': current_node.text,
'response': ''}
current_category['responses'].append(current_response)
elif current_node.tag == 'p':
# The text for a response.
text = current_node.text_content().strip()
if text and current_response:
current_response['response'] = text
# Ignore any other tags that come through.
current_node = current_node.getnext()
return replies

Просмотреть файл

@ -6,32 +6,92 @@ from django.core.cache import cache
from nose.tools import eq_
from pyquery import PyQuery as pq
from customercare.replies import REPLIES_DOCUMENT_SLUG
from sumo.urlresolvers import reverse
from sumo.tests import TestCase
from wiki.tests import document, revision
CANNED_RESPONSES_WIKI = """
Any initial text above the first H1 should be ignored.
=Category 1=
==Reply 1==
Reply goes here http://example.com/kb-article
==Reply 2==
Another reply here
=Category 2=
==Reply 3==
And another reply
"""
MESSED_UP_CANNED_RESPONSES_WIKI = """
Lal al ala la alaa lala la
==Bogus Reply will be ignored==
==Another bogus one==
Any initial text above the first H1 should be ignored.
=Category 1=
==Reply 1==
Reply goes here http://example.com/kb-article
==Reply 2==
Another reply here [[Bad link]]
==A reply without text==
=Category 2=
==Another reply without text==
==Reply 3==
And another reply
==Another Reply without text==
"""
class CannedResponsesTestCase(TestCase):
"""Canned responses tests."""
def _create_doc(self, content):
# Create the canned responses article.
doc = document(slug=REPLIES_DOCUMENT_SLUG, save=True)
rev = revision(
document=doc,
content=content,
is_approved=True,
save=True)
doc.current_revision = rev
doc.save()
def test_list_canned_responses(self):
"""Listing canned responses works as expected."""
# Create the canned responses article.
self._create_doc(CANNED_RESPONSES_WIKI)
r = self.client.get(reverse('customercare.landing'), follow=True)
eq_(200, r.status_code)
doc = pq(r.content)
responses_plain = doc('#accordion').text()
# Listing all categories
assert "Welcome and Thanks" in responses_plain
assert "Using Firefox" in responses_plain
assert "Support" in responses_plain
assert "Get Involved" in responses_plain
# Verify categories and replies
assert 'Category 1' in responses_plain
assert 'Reply 1' in responses_plain
assert 'Reply goes here' in responses_plain
assert 'Category 2' in responses_plain
assert 'Reply 3' in responses_plain
assert 'And another reply' in responses_plain
# Listing all responses
eq_(22, len(doc('#accordion a.reply-topic')))
eq_(3, len(doc('#accordion a.reply-topic')))
def test_list_canned_responses_nondefault_locale(self):
"""Listing canned responses gives all snippets regardless of locale."""
# Create the canned responses article.
self._create_doc(CANNED_RESPONSES_WIKI)
r = self.client.get(reverse('customercare.landing', locale='es'),
follow=True)
@ -39,7 +99,20 @@ class CannedResponsesTestCase(TestCase):
doc = pq(r.content)
# Listing all responses, l10n-agnostic (English if not in Verbatim).
eq_(22, len(doc('#accordion a.reply-topic')))
eq_(3, len(doc('#accordion a.reply-topic')))
def test_messed_up_canned_responses(self):
"""Make sure we don't blow up if the article is malformed."""
# Create the canned responses article.
self._create_doc(MESSED_UP_CANNED_RESPONSES_WIKI)
r = self.client.get(reverse('customercare.landing'), follow=True)
eq_(200, r.status_code)
doc = pq(r.content)
responses_plain = doc('#accordion').text()
assert 'Category 1' in responses_plain
assert 'Category 2' in responses_plain
class TweetListTestCase(TestCase):

Просмотреть файл

@ -19,6 +19,7 @@ from tower import ugettext as _, ugettext_lazy as _lazy
import tweepy
from customercare.models import Tweet, Reply
from customercare.replies import get_common_replies
from sumo.redis_utils import redis_client, RedisError
import twitter
@ -32,148 +33,6 @@ FILTERS = SortedDict([('recent', _lazy('Most Recent')),
('all', _lazy('All'))])
CANNED_RESPONSES = [
{'title': _lazy("Welcome and Thanks"),
'responses':
[{'title': _lazy("Thanks for using Firefox"),
# L10n: This is a reply tweet, so it must fit in 140 characters.
'response': _lazy("thanks for using Firefox! You're not just a user, "
"but part of a community that's 400M strong "
"http://mzl.la/e8xdv5")
},
{'title': _lazy("Tips & tricks"),
# L10n: This is a reply tweet, so it must fit in 140 characters.
'response': _lazy("getting started with Firefox? Here are some tips "
"& tricks for getting the most out of it "
"http://mzl.la/c0B9P2")
},
{'title': _lazy("We're a non-profit organization"),
# L10n: This is a reply tweet, so it must fit in 140 characters.
'response': _lazy("hey, I'm a Mozilla volunteer. Did you know there "
"are 1000s of us worldwide? More here "
"http://mzl.la/cvlwvd")
},
{'title': _lazy("Welcome to our community"),
# L10n: This is a reply tweet, so it must fit in 140 characters.
'response': _lazy("Thanx for joining Moz! You're now part of our "
"global community. We're here if you need help "
"http://mzl.la/bMDof6")
}]
},
{'title': _lazy("Using Firefox"),
'responses':
[{'title': _lazy("Add-on reviews"),
# L10n: This is a reply tweet, so it must fit in 140 characters.
'response': _lazy("getting started with Firefox? Add-ons personalize it"
" w cool features & function. Some faves "
"http://mzl.la/cGypVI")
},
{'title': _lazy("Customize Firefox with add-ons"),
# L10n: This is a reply tweet, so it must fit in 140 characters.
'response': _lazy("have you tried add-ons? Cool apps for shopping, "
"music, news, whatever you do online. Start here: "
"http://mzl.la/blOuoD")
},
{'title': _lazy("Firefox Panorama"),
# L10n: This is a reply tweet, so it must fit in 140 characters.
'response': _lazy("heard about Firefox Panorama? It groups + displays"
" your tabs, eliminating clutter. Try it "
"http://mzl.la/d21MyY")
},
{'title': _lazy("Firefox Sync"),
# L10n: This is a reply tweet, so it must fit in 140 characters.
'response': _lazy("tried Firefox Sync? It's awesome! Switch computers"
" & it saves open tabs, pwords, history. Try it"
" http://mzl.la/aHHUYA")
},
{'title': _lazy("Update plugins and add-ons"),
# L10n: This is a reply tweet, so it must fit in 140 characters.
'response': _lazy("have you updated your plug-ins and add-ons? Should"
" work out the kinks. Here's the place to refresh "
"http://mzl.la/cGCg12")
},
{'title': _lazy("Upgrade Firefox"),
# L10n: This is a reply tweet, so it must fit in 140 characters.
'response': _lazy("hey, maybe you need to upgrade Firefox? New "
"version is speedier with a lot more going on: "
"http://mzl.la/9wJe30")
}]
},
{'title': _lazy("Support"),
'responses':
[{'title': _lazy("Ask SUMO"),
# L10n: This is a reply tweet, so it must fit in 140 characters.
'response': _lazy("maybe ask SUMO about this issue? Firefox's community"
" support team. They'll know what's up: "
"http://mzl.la/bMDof6")
},
{'title': _lazy("Firefox doesn't behave"),
# L10n: This is a reply tweet, so it must fit in 140 characters.
'response': _lazy("sorry your Firefox doesn't behave. Check out the "
"tips here http://mzl.la/bNps7F")
},
{'title': _lazy("Firefox is slow"),
# L10n: This is a reply tweet, so it must fit in 140 characters.
'response': _lazy("you can make your Firefox fast again. Try out "
"these steps http://mzl.la/9bB1FY")
},
{'title': _lazy("Fix crashes"),
# L10n: This is a reply tweet, so it must fit in 140 characters.
'response': _lazy("sorry your Firefox is hanging :( Here are quick "
"fixes to prevent this again http://mzl.la/atSsFt")
},
{'title': _lazy("High RAM usage"),
# L10n: This is a reply tweet, so it must fit in 140 characters.
'response': _lazy("Firefox sometimes uses more memory than it should."
" Try one of these easy fixes http://mzl.la/fPTNo8")
},
{'title': _lazy("Quick Firefox fixes"),
# L10n: This is a reply tweet, so it must fit in 140 characters.
'response': _lazy("have you tried Firefox support? If their quick "
"fixes don't help, volunteers can assist! "
"http://mzl.la/9V9uWd")
},
{'title': _lazy("Slow Firefox startup"),
# L10n: This is a reply tweet, so it must fit in 140 characters.
'response': _lazy("Firefox needs a refresh. Here are tips to make "
"Firefox load faster http://mzl.la/r0mGyN")
}]
},
{'title': _lazy("Get Involved"),
'responses':
[{'title': _lazy("Become a beta tester"),
# L10n: This is a reply tweet, so it must fit in 140 characters.
'response': _lazy("become a beta tester! Help develop the next Firefox."
"You don't have to be a techie to contribute: "
"http://mzl.la/d23n7a")
},
{'title': _lazy("Get involved with Mozilla"),
# L10n: This is a reply tweet, so it must fit in 140 characters.
'response': _lazy("Want a better web? Join the Mozilla movement. "
"There is something to do for everyone. Get started"
" http://mzl.la/cufJmX")
},
{'title': _lazy("Join Drumbeat"),
# L10n: This is a reply tweet, so it must fit in 140 characters.
'response': _lazy("want to spark a movement? Mozilla Drumbeat is your"
" chance to keep the web open and free. More info "
"http://mzl.la/aIXCLA")
},
{'title': _lazy("Mozilla Developer Network"),
# L10n: This is a reply tweet, so it must fit in 140 characters.
'response': _lazy("help make the web better! Build web pages, apps "
"& add-ons here: Mozilla Developer Network "
"http://mzl.la/9gQfrn")
},
{'title': _lazy("Report a bug"),
# L10n: This is a reply tweet, so it must fit in 140 characters.
'response': _lazy("Thanks for finding a bug. Make everyone's Firefox"
" experience better by reporting. It's easy "
"http://mzl.la/bcujVc")
}]
}]
def _tweet_for_template(tweet, https=False):
"""Return the dict needed for tweets.html to render a tweet + replies."""
data = json.loads(tweet.raw_json)
@ -313,7 +172,7 @@ def landing(request):
return jingo.render(request, 'customercare/landing.html', {
'activity_stats': activity_stats,
'contributor_stats': contributor_stats,
'canned_responses': CANNED_RESPONSES,
'canned_responses': get_common_replies(request.locale),
'tweets': _get_tweets(locale=request.locale,
https=request.is_secure()),
'authed': twitter.authed,

Просмотреть файл

@ -27,7 +27,7 @@ Update settings_local.py
Set the following settings in settings_local.py::
TWITTER_CONSUMER_KEY = <consumer key>
TWITTER_CONSUMER_SECRET = <consumer secret>
TWITTER_CONSUMER_SECRET = <consumer secret>
TWITTER_COOKIE_SECURE = False
@ -40,3 +40,28 @@ To fetch tweets, run::
You should now see tweets at /army-of-awesome.
Common replies
==============
Common replies should be defined in a wiki article with slug
'army-of-awesome-common-replies'. The format for the article
content is::
=Category 1=
==Reply 1==
Reply goes here http://example.com/kb-article
==Reply 2==
Another reply here
=Category 2=
==Reply 3==
And another reply
Note that replies can't be over 140 characters long, including the
@username and #fxhelp hash tag. Therefore, each reply defined here
should not go over 125 characters. The reply must be plain text
without any HTML or wiki syntax.