diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..4d726fa --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,27 @@ +name: feedbackxblock + +on: + push: + branches: [master, main] + pull_request: + +jobs: + test: + runs-on: ubuntu-18.04 + strategy: + matrix: + python-version: [3.5, 3.8] + tox-env: + - quality + - py + steps: + - uses: actions/checkout@v2 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.python-version }} + - name: Install dependencies + run: | + pip install tox + - name: Test with tox + run: tox -e ${{ matrix.tox-env }} diff --git a/.gitignore b/.gitignore index 915fc00..e3b41d6 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,7 @@ *pyc rate_xblock.egg-info -*~ \ No newline at end of file +*~ +.coverage +.tox +*.egg-info +__pycache__ \ No newline at end of file diff --git a/README.md b/README.md index 263a9d8..fad64ec 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,9 @@ The goal of this XBlock is two-fold: Instructors have a good amount of control over the contents of the block: -![Screenshot of the FeedbackXBlock](FeedbackXBlock.png) +![Screenshot of the FeedbackXBlock -- good to bad scale](happy_sad_example.png) +![Screenshot of the FeedbackXBlock -- scale where good is in the middle](happy_sad_happy_example.png) +![Screenshot of the FeedbackXBlock -- numerical scale](numerical_example.png) This can be placed anywhere in the courseware, and students can provide feedback on those sections. With just a few database queries, @@ -26,5 +28,5 @@ It installs on any Open edX install same as any other xblock: pip install -e git+https://github.com/pmitros/FeedbackXBlock.git#egg=rate==0.0 -From there, add 'feedback' to your list of advanced modules, and you're +From there, add "feedback" to your list of advanced modules, and you're good to go. diff --git a/feedback/__init__.py b/feedback/__init__.py index b654b63..a7e02e6 100644 --- a/feedback/__init__.py +++ b/feedback/__init__.py @@ -1 +1,5 @@ -from .feedback import FeedbackXBlock +""" +An edX XBlock designed to allow people to provide feedback on our +course resources, and to think and synthesize about their experience +in the course. +""" diff --git a/feedback/feedback.py b/feedback/feedback.py index 6bed498..d758b53 100644 --- a/feedback/feedback.py +++ b/feedback/feedback.py @@ -5,14 +5,15 @@ in the course. """ -import cgi +import html import random - import pkg_resources +import six from xblock.core import XBlock from xblock.fields import Scope, Integer, String, List, Float -from xblock.fragment import Fragment +from web_fragments.fragment import Fragment + # We provide default text which is designed to elicit student thought. We'd # like instructors to customize this to something highly structured (not @@ -23,14 +24,15 @@ "lessons learned, as well as key gaps in our presentation." DEFAULT_PLACEHOLDER = "Take a little bit of time to reflect here. " \ "Research shows that a meaningful synthesis will help " \ - "you better understand and remember material from this" \ + "you better understand and remember material from this " \ "course." DEFAULT_ICON = "face" DEFAULT_SCALETEXT = ["Excellent", "Good", "Average", "Fair", "Poor"] -ICON_SETS = {'face': u"😁😊😐😞😭", +# Unicode alt faces are cute, but we do nulls instead for a11y. +ICON_SETS = {'face': [""]*5, # u"😁😊😐😞😭", 'num': u"12345", - 'midface': u"😞😐😊😐😞"} + 'midface': [""]*5} # u"😞😐😊😐😞"} @XBlock.needs('i18n') @@ -221,8 +223,7 @@ def get_url(icon_type, i): votes, act_urls, ina_urls, - sel_urls - ) + sel_urls) ) if self.user_vote != -1: _ = self.runtime.service(self, 'i18n').ugettext @@ -264,9 +265,9 @@ def studio_view(self, context): prompt = self.get_prompt(0) for idx in range(len(prompt['scale_text'])): prompt['likert{i}'.format(i=idx)] = prompt['scale_text'][idx] - frag = Fragment(unicode(html_str).format(**prompt)) + frag = Fragment(six.text_type(html_str).format(**prompt)) js_str = self.resource_string("static/js/src/studio.js") - frag.add_javascript(unicode(js_str)) + frag.add_javascript(six.text_type(js_str)) frag.initialize_js('FeedbackBlock', {'icon_set': prompt['icon_set']}) return frag @@ -276,20 +277,15 @@ def studio_submit(self, data, suffix=''): """ Called when submitting the form in Studio. """ - print "Received: ", data - print "Old prompt: ", self.prompts[0] for item in ['freeform', 'likert', 'placeholder', 'icon_set']: item_submission = data.get(item, None) if item_submission and len(item_submission) > 0: - print "Setting", item - self.prompts[0][item] = cgi.escape(item_submission) + self.prompts[0][item] = html.escape(item_submission) for i in range(5): likert = data.get('likert{i}'.format(i=i), None) if likert and len(likert) > 0: - print "Setting", i - self.prompts[0]['scale_text'][i] = cgi.escape(likert) + self.prompts[0]['scale_text'][i] = html.escape(likert) - print "New prompt: ", self.prompts[0] return {'result': 'success'} def init_vote_aggregate(self): @@ -309,7 +305,7 @@ def vote(self, data): """ # prompt_choice is initialized by student view. # Ideally, we'd break this out into a function. - prompt = self.get_prompt(self.prompt_choice) + prompt = self.get_prompt(self.prompt_choice) # noqa # Make sure we're initialized self.init_vote_aggregate() diff --git a/feedback/static/html/feedback.html b/feedback/static/html/feedback.html index 8f55ba7..7048c45 100644 --- a/feedback/static/html/feedback.html +++ b/feedback/static/html/feedback.html @@ -9,7 +9,7 @@
- +
{response}
diff --git a/feedback/static/html/scale_item.html b/feedback/static/html/scale_item.html index 15bc4ee..187b2a5 100644 --- a/feedback/static/html/scale_item.html +++ b/feedback/static/html/scale_item.html @@ -1,6 +1,6 @@