diff --git a/xblocks_contrib/word_cloud/static/js/src/word_cloud.js b/xblocks_contrib/word_cloud/static/js/src/word_cloud.js index 728e652..475f1fd 100644 --- a/xblocks_contrib/word_cloud/static/js/src/word_cloud.js +++ b/xblocks_contrib/word_cloud/static/js/src/word_cloud.js @@ -27,44 +27,20 @@ function generateUniqueId(wordCloudId, counter) { } function WordCloudBlock(runtime, element) { - - // eslint-disable-next-line no-undef - const wordCloudEl = $(element).find(blockIdentifier); - - // Get the URL to which we will post the users words. - const ajax_url = wordCloudEl.data('ajax-url'); - - // Hide WordCloud container until Ajax request is complete. - wordCloudEl.hide(); - - // Fetch initial state via AJAX request. Attach a callback that will - // be fired on server's response. - // eslint-disable-next-line no-undef - $.postWithPrefix( - `${ajax_url}/get_state`, - null, - (response) => { - if (response.status !== 'success') { - console.error('Failed to fetch state'); - return; + $.ajax({ + type: "POST", + url: runtime.handlerUrl(element, 'handle_get_state'), + data: JSON.stringify(null), + success: function (response) { + if (response && response.submitted) { + showWordCloud(response, element); } - - configJson = response; - if (configJson && configJson.submitted) { - showWordCloud(configJson, wordCloudEl); - } - - }, - ) - .done(() => { - // Show WordCloud container after Ajax request done - wordCloudEl.show(); - }); - - // eslint-disable-next-line no-undef - $(element).find('.save').on('click', () => { - submitAnswer(ajax_url, wordCloudEl); + } }); + + $('.save', element).on('click', () => + submitAnswer(runtime, element) + ); } /** @@ -74,32 +50,23 @@ function WordCloudBlock(runtime, element) { * server, and upon receiving correct response, will call the function to generate the * word cloud. */ -function submitAnswer(ajax_url, wordCloudEl) -{ +function submitAnswer(runtime, element) { + const wordCloudEl = $(element).find(blockIdentifier); const data = {student_words: []}; - // Populate the data to be sent to the server with user's words. wordCloudEl.find('input.input-cloud').each((index, value) => { // eslint-disable-next-line no-undef data.student_words.push($(value).val()); }); - // Send the data to the server as an AJAX request. Attach a callback that will - // be fired on server's response. - // eslint-disable-next-line no-undef - $.postWithPrefix( - `${ajax_url}/submit`, - // eslint-disable-next-line no-undef - $.param(data), - (response) => { - if (response.status !== 'success') { - console.error('Submission failed'); - return; - } - - showWordCloud(response, wordCloudEl); - }, - ); + $.ajax({ + type: "POST", + url: runtime.handlerUrl(element, 'handle_submit_state'), + data: JSON.stringify(data), + success: function (response) { + showWordCloud(response, element); + } + }); } /** @@ -111,7 +78,7 @@ function submitAnswer(ajax_url, wordCloudEl) * This function will set up everything for d3 and launch the draw method. Among other things, * iw will determine maximum word size. */ -function showWordCloud(response, wordCloudEl) +function showWordCloud(response, element) { const words = response.top_words; let maxSize = 0; @@ -120,6 +87,7 @@ function showWordCloud(response, wordCloudEl) let maxFontSize = 200; const minFontSize = 16; + const wordCloudEl = $(element).find(blockIdentifier); wordCloudEl.find('.input_cloud_section').hide(); // Find the word with the maximum percentage. I.e. the most popular word. diff --git a/xblocks_contrib/word_cloud/templates/word_cloud.html b/xblocks_contrib/word_cloud/templates/word_cloud.html index 7c57b37..ec009a5 100644 --- a/xblocks_contrib/word_cloud/templates/word_cloud.html +++ b/xblocks_contrib/word_cloud/templates/word_cloud.html @@ -2,8 +2,7 @@
+ class="word_cloud_block"> {% if display_name %}

{{ display_name }}

{% endif %} diff --git a/xblocks_contrib/word_cloud/word_cloud.py b/xblocks_contrib/word_cloud/word_cloud.py index d40ebde..387625f 100644 --- a/xblocks_contrib/word_cloud/word_cloud.py +++ b/xblocks_contrib/word_cloud/word_cloud.py @@ -13,8 +13,6 @@ from xblock.utils.resources import ResourceLoader from xblock.utils.studio_editable import StudioEditableXBlockMixin -from xblocks_contrib.utils.mixins.x_module import XModuleToXBlockMixin - resource_loader = ResourceLoader(__name__) @@ -37,11 +35,7 @@ def pretty_bool(value): return value in bool_dict -class WordCloudBlock( - StudioEditableXBlockMixin, - XBlock, - XModuleToXBlockMixin, -): +class WordCloudBlock(StudioEditableXBlockMixin, XBlock): """ Word Cloud XBlock. """ @@ -135,7 +129,6 @@ def student_view(self, context=None): # pylint: disable=W0613 frag = Fragment() frag.add_content(resource_loader.render_django_template( "templates/word_cloud.html", { - 'ajax_url': self.ajax_url, 'display_name': self.display_name, 'instructions': self.instructions, 'element_id': self.scope_ids.usage_id.html_id(), @@ -176,6 +169,92 @@ def top_dict(self, dict_obj, amount): )[:amount] ) + def get_state(self): + """Return success json answer for client.""" + if self.submitted: + total_count = sum(self.all_words.values()) + return { + 'status': 'success', + 'submitted': True, + 'display_student_percents': pretty_bool( + self.display_student_percents + ), + 'student_words': { + word: self.all_words[word] for word in self.student_words + }, + 'total_count': total_count, + 'top_words': self.prepare_words(self.top_words, total_count), + } + else: + return { + 'status': 'success', + 'submitted': False, + 'display_student_percents': False, + 'student_words': {}, + 'total_count': 0, + 'top_words': {} + } + + @XBlock.json_handler + def handle_get_state(self, data, suffix=''): # pylint: disable=unused-argument + """ + AJAX handler to get the current state of the XBlock + + Args: + data: dict having request get parameters + + Returns: + json string + """ + return self.get_state() + + @XBlock.json_handler + def handle_submit_state(self, data, suffix=''): # pylint: disable=unused-argument + """ + AJAX handler to submit the current state of the XBlock + + Args: + data: dict having request get parameters + + Returns: + json string + """ + + if self.submitted: + return { + 'status': 'fail', + 'error': 'You have already posted your data.' + } + + # Student words from client. + # FIXME: we must use raw JSON, not a post data (multipart/form-data) + raw_student_words = data.get('student_words') + student_words = [word for word in map(self.good_word, raw_student_words) if word] + + self.student_words = student_words + + # FIXME: fix this, when xblock will support mutable types. + # Now we use this hack. + # speed issues + temp_all_words = self.all_words + + self.submitted = True + + # Save in all_words. + for word in self.student_words: + temp_all_words[word] = temp_all_words.get(word, 0) + 1 + + # Update top_words. + self.top_words = self.top_dict( + temp_all_words, + self.num_top_words + ) + + # Save all_words in database. + self.all_words = temp_all_words + + return self.get_state() + def prepare_words(self, top_words, total_count): """Convert words dictionary for client API. @@ -286,29 +365,3 @@ def handle_ajax(self, dispatch, data): 'status': 'fail', 'error': 'Unknown Command!' }) - - def get_state(self): - """Return success json answer for client.""" - if self.submitted: - total_count = sum(self.all_words.values()) - return json.dumps({ - 'status': 'success', - 'submitted': True, - 'display_student_percents': pretty_bool( - self.display_student_percents - ), - 'student_words': { - word: self.all_words[word] for word in self.student_words - }, - 'total_count': total_count, - 'top_words': self.prepare_words(self.top_words, total_count) - }) - else: - return json.dumps({ - 'status': 'success', - 'submitted': False, - 'display_student_percents': False, - 'student_words': {}, - 'total_count': 0, - 'top_words': {} - })