From 267a9b8b23d1143ede353dbd4d7f7bf1b0db27a0 Mon Sep 17 00:00:00 2001 From: Ahmed Bilal Khalid Date: Tue, 4 Aug 2020 10:14:49 +0500 Subject: [PATCH] Make Poll and Survey Indexable (#81) --- poll/poll.py | 78 +++++++++++++++++++++++++++++++++- poll/utils.py | 13 ++++++ requirements.txt | 1 + setup.py | 3 +- tests/unit/test_xblock_poll.py | 33 +++++++++++++- 5 files changed, 125 insertions(+), 3 deletions(-) diff --git a/poll/poll.py b/poll/poll.py index ce666ed..a7c9082 100644 --- a/poll/poll.py +++ b/poll/poll.py @@ -41,7 +41,7 @@ from xblockutils.resources import ResourceLoader from xblockutils.settings import ThemableXBlockMixin, XBlockWithSettingsMixin -from .utils import DummyTranslationService, _ +from .utils import DummyTranslationService, _, remove_markdown_and_html_tags try: # pylint: disable=import-error, bad-option-value, ungrouped-imports @@ -840,6 +840,41 @@ def generate_report_data(self, user_state_iterator, limit_responses=None): count += 1 yield (user_state.username, report) + def index_dictionary(self): + """ + Return dictionary prepared with module content and type for indexing. + """ + # return key/value fields in a Python dict object + # values may be numeric / string or dict + # default implementation is an empty dict + xblock_body = super(PollBlock, self).index_dictionary() + answers = { + "option_{}".format(answer_i): remove_markdown_and_html_tags(answer[1]['label']) + for answer_i, answer in enumerate(self.answers) + if len(answer) == 2 and 'label' in answer[1] and answer[1]['label'] + } + image_alt_text = { + "image_alt_{}".format(answer_i): answer[1]['img_alt'] + for answer_i, answer in enumerate(self.answers) + if len(answer) == 2 and 'img_alt' in answer[1] and answer[1]['img_alt'] + } + + index_body = { + "display_name": self.display_name, + "question": remove_markdown_and_html_tags(self.question), + } + index_body.update(answers) + index_body.update(image_alt_text) + + if "content" in xblock_body: + xblock_body["content"].update(index_body) + else: + xblock_body["content"] = index_body + + xblock_body["content_type"] = "Poll" + + return xblock_body + @XBlock.wants('settings') @XBlock.needs('i18n') @@ -1357,3 +1392,44 @@ def generate_report_data(self, user_state_iterator, limit_responses=None): } count += 1 yield (user_state.username, report) + + def index_dictionary(self): + """ + Return dictionary prepared with module content and type for indexing. + """ + # return key/value fields in a Python dict object + # values may be numeric / string or dict + # default implementation is an empty dict + xblock_body = super(SurveyBlock, self).index_dictionary() + + questions = { + "question_{}".format(question_i): remove_markdown_and_html_tags(question[1]['label']) + for question_i, question in enumerate(self.questions) + if len(question) == 2 and 'label' in question[1] and question[1]['label'] + } + answers = { + "option_{}".format(answer_i): remove_markdown_and_html_tags(answer[1]) + for answer_i, answer in enumerate(self.answers) + if len(answer) == 2 and answer[1] + } + image_alt_text = { + "image_alt_{}".format(question_i): question[1]['img_alt'] + for question_i, question in enumerate(self.questions) + if len(question) == 2 and 'img_alt' in question[1] and question[1]['img_alt'] + } + + index_body = { + "display_name": self.display_name, + } + index_body.update(questions) + index_body.update(answers) + index_body.update(image_alt_text) + + if "content" in xblock_body: + xblock_body["content"].update(index_body) + else: + xblock_body["content"] = index_body + + xblock_body["content_type"] = "Survey" + + return xblock_body diff --git a/poll/utils.py b/poll/utils.py index 16be1cb..d9ef068 100644 --- a/poll/utils.py +++ b/poll/utils.py @@ -1,5 +1,7 @@ # -*- coding: utf-8 -*- # +from bleach.sanitizer import Cleaner +from markdown import markdown # Make '_' a no-op so we can scrape strings @@ -12,6 +14,17 @@ def ngettext_fallback(text_singular, text_plural, number): return text_singular if number == 1 else text_plural +def remove_html_tags(data): + """ Remove html tags from provided data """ + cleaner = Cleaner(tags=[], strip=True) + return cleaner.clean(data) + + +def remove_markdown_and_html_tags(data): + """ Remove both markdown and html tags from provided data """ + return remove_html_tags(markdown(data)) + + class DummyTranslationService(object): # pylint: disable=bad-option-value """ Dummy drop-in replacement for i18n XBlock service diff --git a/requirements.txt b/requirements.txt index fd7e9a7..550bc61 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,4 +3,5 @@ markdown ddt mock django-nose==1.4.6 +bleach==3.1.5 -e . diff --git a/setup.py b/setup.py index 4a3fabb..340d7d9 100644 --- a/setup.py +++ b/setup.py @@ -46,7 +46,7 @@ def package_data(pkg, roots): setup( name='xblock-poll', - version='1.9.10', + version='1.10.0', description='An XBlock for polling users.', packages=[ 'poll', @@ -56,6 +56,7 @@ def package_data(pkg, roots): 'markdown', 'ddt', 'mock', + 'bleach', ], entry_points={ 'xblock.v1': [ diff --git a/tests/unit/test_xblock_poll.py b/tests/unit/test_xblock_poll.py index 8a8b26c..6dd2933 100644 --- a/tests/unit/test_xblock_poll.py +++ b/tests/unit/test_xblock_poll.py @@ -114,7 +114,21 @@ def test_generate_report_data_limit_responses(self): report_data = list(report_data) self.assertEqual(len(report_data), 0) - + def test_indexing(self): + self.assertEqual( + self.poll_block.index_dictionary(), + { + 'content': { + 'display_name': 'My Poll', + 'question': 'What is your favorite color?', + 'option_0': 'Red', + 'option_1': 'Blue', + 'option_2': 'Green', + 'option_3': 'Other', + }, + 'content_type': 'Poll' + } + ) class TestSurveyBlock(unittest.TestCase): """ Tests for XBlock Survey. @@ -270,3 +284,20 @@ def test_generate_report_data_limit_responses(self): report_data = self.survey_block.generate_report_data(user_states, limit_responses=0) report_data = list(report_data) self.assertEqual(len(report_data), 0) + + def test_indexing(self): + self.assertEqual( + self.survey_block.index_dictionary(), + { + 'content': { + 'display_name': 'My Survey', + 'question_0': 'Are you enjoying the course?', + 'question_1': 'Would you recommend this course to your friends?', + 'question_2': 'Do you think you will learn a lot?', + 'option_0': 'Yes', + 'option_1': 'No', + 'option_2': 'Maybe', + }, + 'content_type': 'Survey' + } + )