Skip to content

Commit

Permalink
chore: Drop Xmodule mixin, update ajax to latest pattern
Browse files Browse the repository at this point in the history
  • Loading branch information
farhan committed Jan 17, 2025
1 parent 9194a08 commit e5b2f9f
Show file tree
Hide file tree
Showing 3 changed files with 112 additions and 92 deletions.
80 changes: 24 additions & 56 deletions xblocks_contrib/word_cloud/static/js/src/word_cloud.js
Original file line number Diff line number Diff line change
Expand Up @@ -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)
);
}

/**
Expand All @@ -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);
}
});
}

/**
Expand All @@ -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;
Expand All @@ -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.
Expand Down
3 changes: 1 addition & 2 deletions xblocks_contrib/word_cloud/templates/word_cloud.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@

<div
id="word_cloud_{{ element_id }}"
class="word_cloud_block"
data-ajax-url="{{ ajax_url }}">
class="word_cloud_block">
{% if display_name %}
<h3 class="hd hd-3" id="word_cloud_{{ element_id }}_heading">{{ display_name }}</h3>
{% endif %}
Expand Down
121 changes: 87 additions & 34 deletions xblocks_contrib/word_cloud/word_cloud.py
Original file line number Diff line number Diff line change
Expand Up @@ -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__)


Expand All @@ -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.
"""
Expand Down Expand Up @@ -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(),
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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': {}
})

0 comments on commit e5b2f9f

Please sign in to comment.