5.1. Customize myxblock.py#

This section describes how to modify the Python file of the XBlock you created, myxblock.py, to provide the functionality in the Thumbs XBlock example in the XBlock SDK.

In myxblock.py, you will define fields, views, handlers, and workbench scenarios.

5.1.1. The Default XBlock Python File#

When you create a new XBlock, the default Python file is created automatically, with skeletal functionality defined. In the xblock_development/myxblock/myxblock/ directory, see the file myxblock.py.

"""TO-DO: Write a description of what this XBlock is."""

import pkg_resources

from xblock.core import XBlock
from xblock.fields import Scope, Integer
from xblock.fragment import Fragment


class MyXBlock(XBlock):
    """
    TO-DO: document what your XBlock does.
    """

    # Fields are defined on the class.  You can access them in your code as
    # self.<fieldname>.

    # TO-DO: delete count, and define your own fields.
    count = Integer(
        default=0, scope=Scope.user_state,
        help="A simple counter, to show something happening",
    )

    def resource_string(self, path):
        """Handy helper for getting resources from our kit."""
        data = pkg_resources.resource_string(__name__, path)
        return data.decode("utf8")

    # TO-DO: change this view to display your data your own way.
    def student_view(self, context=None):
        """
        The primary view of the MyXBlock, shown to students
        when viewing courses.
        """
        html = self.resource_string("static/html/myxblock.html")
        frag = Fragment(html.format(self=self))
        frag.add_css(self.resource_string("static/css/myxblock.css"))
        frag.add_javascript(self.resource_string("static/js/src/myxblock.js"))
        frag.initialize_js('MyXBlock')
        return frag

    # TO-DO: change this handler to perform your own actions.  You may need more
    # than one handler, or you may not need any handlers at all.
    @XBlock.json_handler
    def increment_count(self, data, suffix=''):
        """
        An example handler, which increments the data.
        """
        # Just to show data coming in...
        assert data['hello'] == 'world'

        self.count += 1
        return {"count": self.count}

    # TO-DO: change this to create the scenarios you'd like to see in the
    # workbench while developing your XBlock.
    @staticmethod
    def workbench_scenarios():
        """A canned scenario for display in the workbench."""
        return [
            ("MyXBlock",
             """<vertical_demo>
                <myxblock/>
                <myxblock/>
                <myxblock/>
                </vertical_demo>
             """),
        ]

5.1.2. Add Comments#

As a best practice and because XBlocks can be shared, you should add comments to the myxblock.py file. Replace the “TO DO” indicators with a description of what the XBlock does and any details future developers or users would want to know.

5.1.3. Add XBlock Fields#

You determine the data your XBlock stores through fields. Fields store user and XBlock state as JSON data.

To customize your myxblock.py file so that it has the same functionality as the thumbs.py file, you need to add three fields to the XBlock, each with the right scope.

  • upvotes, to store the number of times users up-vote the XBlock. The value applies to the XBlock and all users collectively.

  • downvotes, to store the number of times users down-vote the XBlock. The value applies to the XBlock and all users collectively.

  • voted, to record whether or not the user has voted. The value applies to the XBlock and each user individually.

Review the XBlock Fields section, then add the required fields to myxblock.py. You can remove the count field, which was defined automatically when you created the XBlock.

5.1.3.1. Check Fields Against the Thumbs XBlock#

After you have defined the fields, check your work against the fields in the Thumbs XBlock, in the file xblock_development/xblock-sdk/sample_xblocks/thumbs/thumbs.py.

class ThumbsBlockBase(object):
    upvotes = Integer(help="Number of up votes", default=0,
        scope=Scope.user_state_summary)
    downvotes = Integer(help="Number of down votes", default=0,
        scope=Scope.user_state_summary)
    voted = Boolean(help="Has this student voted?", default=False,
        scope=Scope.user_state)

If necessary, make corrections to the fields in your XBlock so that they match the fields in the Thumbs XBlock.

Note the following details.

  • upvotes and downvotes have the scope Scope.user_state_summary. This indicates that the data in these fields are specific to the XBlock and the same for all users.

  • voted has the scope Scope.user_state. This indicates that the data in this field applies to the XBlock and to the specific user.

5.1.4. Define the Student View#

The XBlock Python file must contain one or more view methods.

To run the XBlock in the edX Platform Learning Management System, there must be a method named student_view. If you intend the XBlock to run in a different runtime application, you might need to define a different name. For more information, see EdX Learning Management System as an XBlock Runtime.

In myxblock.py, examine the student_view method that was defined automatically when you created the XBlock.

The student view composes and returns the fragment from static HTML, JavaScript, and CSS files. A web page displays the fragment to learners.

Note the following details about student view.

  • The static HTML is added when the fragment is initialized.

    html = self.resource_string("static/html/myxblock.html")
    frag = Fragment(unicode(html_str).format(self=self))
    
  • The JavaScript and CSS files are added to the fragment with the add_javascript() and add_css methods.

  • The JavaScript in the fragment must be initialized using the name of the XBlock class. The name also maps to the function that initializes the XBlock in the JavaScript file.

    frag.initialize_js('MyXBlock')
    

As you can see, the necessary functions of the view were added automatically. Check the student view in myxblock.py against the student view in thumbs.py. Note that the only differences are the file names of the HTML, CSS, and JavaScript files added to the fragment. As the file names are correct for MyXBlock, you do not need to edit the student view at all.

5.1.5. Define the Vote Handler#

Handlers process input events from the XBlock JavaScript code. You use handlers to add interactivity to your block. In your XBlock, you use a handler to process votes from users.

The vote handler in your XBlock must perform the following functions.

  1. Update upvotes or downvotes fields based on the user’s vote.

  2. Set the voted field to True for the user.

  3. Return the updated upvotes and downvotes fields.

Review the XBlock Methods section, then implement the vote handler in myxblock.py.

You can use any name for the vote handler, and you will use the same name in the JavaScript code to connect browser events to the vote handler running in the server. To match the Thumbs XBlock, use the name vote.

5.1.5.1. Check the Handler Against the Thumbs XBlock#

After you have defined the vote handler, check your work against the handler in the Thumbs XBlock.

@XBlock.json_handler
def vote(self, data, suffix=''):  # pylint: disable=unused-argument
    """
    Update the vote count in response to a user action.
    """
    # Here is where we would prevent a student from voting twice, but then
    # we couldn't click more than once in the demo!
    #
    #     if self.voted:
    #         log.error("cheater!")
    #         return

    if data['voteType'] not in ('up', 'down'):
        log.error('error!')
        return

    if data['voteType'] == 'up':
        self.upvotes += 1
    else:
        self.downvotes += 1

    self.voted = True

    return {'up': self.upvotes, 'down': self.downvotes}

If necessary, make corrections to the handler in your XBlock so that it matches the handler in the Thumbs XBlock.

5.1.6. Next Step#

After you complete your customizations to the Python file, you continue on and customize the XBlock HTML file.