diff --git a/.gitignore b/.gitignore index 3d5fe8e1..96d74d46 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,4 @@ node_modules/ .coverage coverage.xml .tox/ +.DS_STORE diff --git a/Gruntfile.js b/Gruntfile.js index a11d6991..2ba42b6c 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -8,7 +8,6 @@ module.exports = function (grunt) { var directoryPublic = directoryPackage + '/public'; var directoryPrivateJsAll = directoryPrivate + '/**/*.js'; var directoryPrivateLessAll = directoryPrivate + '/**/*.less'; - var directoryPrivateHtmlAll = directoryPrivate + '/**/*.html'; var directoryPublicCssAll = directoryPublic + '/**/*.css'; grunt.initConfig({ @@ -71,21 +70,6 @@ module.exports = function (grunt) { }], }, }, - htmlmin: { - all: { - options: { - removeComments: true, - removeCommentsFromCDATA: true, - collapseWhitespace: true, - collapseBooleanAttributes: true, - removeRedundantAttributes: true, - removeEmptyAttributes: true, - }, - files: { - 'freetextresponse/public/view.html': directoryPrivate + '/view.html', - }, - }, - }, jshint: { options: { ignores: [ @@ -154,7 +138,6 @@ module.exports = function (grunt) { grunt.loadNpmTasks('grunt-contrib-watch'); grunt.loadNpmTasks('grunt-contrib-copy'); grunt.loadNpmTasks('grunt-contrib-clean'); - grunt.loadNpmTasks('grunt-contrib-htmlmin'); grunt.registerTask('default', [ 'jshint', @@ -163,6 +146,5 @@ module.exports = function (grunt) { 'less', 'csslint', 'uglify', - 'htmlmin', ]); }; diff --git a/conf/locale b/conf/locale new file mode 120000 index 00000000..1b356a91 --- /dev/null +++ b/conf/locale @@ -0,0 +1 @@ +../freetextresponse/locale/ \ No newline at end of file diff --git a/freetextresponse/freetextresponse.py b/freetextresponse/freetextresponse.py index 2a6301c7..ebd927b9 100644 --- a/freetextresponse/freetextresponse.py +++ b/freetextresponse/freetextresponse.py @@ -1,12 +1,12 @@ """ This is the core logic for the Free-text Response XBlock """ - -import os - -import pkg_resources -from django.utils.translation import ungettext from enum import Enum +from django.template.context import Context +from django.template.loader import get_template +from django.utils.translation import ungettext +from django.utils.translation import ugettext_lazy as _ +from django.utils.translation import ugettext from xblock.core import XBlock from xblock.fields import Boolean from xblock.fields import Float @@ -18,9 +18,8 @@ from xblock.validation import ValidationMessage from xblockutils.studio_editable import StudioEditableXBlockMixin -from .utils import _ - +@XBlock.needs("i18n") class FreeTextResponse(StudioEditableXBlockMixin, XBlock): # pylint: disable=too-many-ancestors, too-many-instance-attributes """ @@ -90,7 +89,7 @@ def workbench_scenarios(): help=_( 'This is the title for this question type' ), - default=_('Free-text Response'), + default='Free-text Response', scope=Scope.settings, ) fullcredit_keyphrases = List( @@ -149,7 +148,7 @@ def workbench_scenarios(): 'This is the prompt students will see when ' 'asked to enter their response' ), - default=_('Please enter your response within this text area'), + default='Please enter your response within this text area', scope=Scope.settings, multiline_editor=True, ) @@ -159,7 +158,7 @@ def workbench_scenarios(): 'This is the message students will see upon ' 'submitting their response' ), - default=_('Your submission has been received'), + default='Your submission has been received', scope=Scope.settings, ) weight = Integer( @@ -178,7 +177,7 @@ def workbench_scenarios(): 'This is the message students will see upon ' 'submitting a draft response' ), - default=_( + default=( 'Your answers have been saved but not graded. ' 'Click "Submit" to grade them.' ), @@ -211,38 +210,75 @@ def workbench_scenarios(): 'fullcredit_keyphrases', 'halfcredit_keyphrases', 'submitted_message', + 'saved_message', ) + def build_fragment( + self, + template, + context_dict, + initialize_js_func, + additional_css=[], + additional_js=[], + ): + # pylint: disable=dangerous-default-value, too-many-arguments + """ + Creates a fragment for display. + """ + context = Context(context_dict) + fragment = Fragment(template.render(context)) + for item in additional_css: + url = self.runtime.local_resource_url(self, item) + fragment.add_css_url(url) + for item in additional_js: + url = self.runtime.local_resource_url(self, item) + fragment.add_javascript_url(url) + fragment.initialize_js(initialize_js_func) + return fragment + # Decorate the view in order to support multiple devices e.g. mobile # See: https://openedx.atlassian.net/wiki/display/MA/Course+Blocks+API # section 'View @supports(multi_device) decorator' @XBlock.supports('multi_device') - def student_view(self, context=None): - # pylint: disable=unused-argument + def student_view(self, context={}): + # pylint: disable=dangerous-default-value + """The main view of FreeTextResponse, displayed when viewing courses. + + The main view which displays the general layout for FreeTextResponse + + Args: + context: Not used for this view. + + Returns: + (Fragment): The HTML Fragment for this XBlock, which determines the + general frame of the FreeTextResponse Question. """ - Build the fragment for the default student view - """ - view_html = FreeTextResponse.get_resource_string('view.html') - view_html = view_html.format( - self=self, - word_count_message=self._get_word_count_message(), - indicator_class=self._get_indicator_class(), - problem_progress=self._get_problem_progress(), - used_attempts_feedback=self._get_used_attempts_feedback(), - nodisplay_class=self._get_nodisplay_class(), - visibility_class=self._get_indicator_visibility_class(), - submitted_message='', - user_alert='', + + self.runtime.service(self, 'i18n') + context.update( + { + 'display_name': self.display_name, + 'indicator_class': self._get_indicator_class(), + 'nodisplay_class': self._get_nodisplay_class(), + 'problem_progress': self._get_problem_progress(), + 'prompt': self.prompt, + 'student_answer': self.student_answer, + 'used_attempts_feedback': self._get_used_attempts_feedback(), + 'visibility_class': self._get_indicator_visibility_class(), + 'word_count_message': self._get_word_count_message(), + } ) + template = get_template('freetextresponse_view.html') fragment = self.build_fragment( - html_source=view_html, - paths_css=[ - 'view.less.min.css', + template, + context, + initialize_js_func='FreeTextResponseView', + additional_css=[ + 'public/view.less.min.css', ], - paths_js=[ - 'view.js.min.js', + additional_js=[ + 'public/view.js.min.js', ], - fragment_js='FreeTextResponseView', ) return fragment @@ -264,7 +300,7 @@ def _generate_validation_message(cls, msg): """ result = ValidationMessage( ValidationMessage.ERROR, - _(unicode(msg)) + ugettext(unicode(msg)) ) return result @@ -298,51 +334,6 @@ def validate_field_data(self, validation, data): ) validation.add(msg) - @classmethod - def get_resource_string(cls, path): - """ - Retrieve string contents for the file path - """ - path = os.path.join('public', path) - resource_string = pkg_resources.resource_string(__name__, path) - return resource_string.decode('utf8') - - def get_resource_url(self, path): - """ - Retrieve a public URL for the file path - """ - path = os.path.join('public', path) - resource_url = self.runtime.local_resource_url(self, path) - return resource_url - - def build_fragment( - self, - html_source=None, - paths_css=[], - paths_js=[], - urls_css=[], - urls_js=[], - fragment_js=None, - ): - # pylint: disable=dangerous-default-value, too-many-arguments - """ - Assemble the HTML, JS, and CSS for an XBlock fragment - """ - fragment = Fragment(html_source) - for url in urls_css: - fragment.add_css_url(url) - for path in paths_css: - url = self.get_resource_url(path) - fragment.add_css_url(url) - for url in urls_js: - fragment.add_javascript_url(url) - for path in paths_js: - url = self.get_resource_url(path) - fragment.add_javascript_url(url) - if fragment_js: - fragment.initialize_js(fragment_js) - return fragment - def _get_indicator_visibility_class(self): """ Returns the visibility class for the correctness indicator html element @@ -379,7 +370,7 @@ def _get_invalid_word_count_message(self, ignore_attempts=False): (not self._word_count_valid()) ): word_count_message = self._get_word_count_message() - result = _( + result = ugettext( "Invalid Word Count. {word_count_message}" ).format( word_count_message=word_count_message, @@ -475,7 +466,6 @@ def _compute_score(self): def _determine_credit(self): # Not a standard xlbock pylint disable. # This is a problem with pylint 'enums and R0204 in general' - # pylint: disable=redefined-variable-type """ Helper Method that determines the level of credit that the user should earn based on their answer diff --git a/freetextresponse/locale/vi/LC_MESSAGES/django.mo b/freetextresponse/locale/vi/LC_MESSAGES/django.mo new file mode 100644 index 00000000..83d92195 Binary files /dev/null and b/freetextresponse/locale/vi/LC_MESSAGES/django.mo differ diff --git a/freetextresponse/locale/vi/LC_MESSAGES/django.po b/freetextresponse/locale/vi/LC_MESSAGES/django.po new file mode 100644 index 00000000..b814bfb6 --- /dev/null +++ b/freetextresponse/locale/vi/LC_MESSAGES/django.po @@ -0,0 +1,48 @@ +# Vietnamese Translations for Free Text Response Xblock +# plural translations are copies of the singular version + +# Copyright (C) 2017 +# This file is distributed under the same license as the free-text-response package. +# Hang Pham , 2017 +# +msgid "" +msgstr "" +"Project-Id-Version: 0.1.2\n" +"Report-Msgid-Bugs-To: mondiaz \n" +"POT-Creation-Date: 2017-06-13 22:13+0000\n" +"PO-Revision-Date: 2017-06-14 22:13+0000\n" +"Last-Translator: mondiaz , 2017\n" +"Language-Team: Vietnamese (https://www.transifex.com/stanford-online/teams/22259/vi/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: vi\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#: freetextresponse/freetextresponse.py:427 +msgid "Your response must be between {min} and {max} word." +msgid_plural "Your response must be between {min} and {max} words." +msgstr[0] "Giới hạn {min}-{max} từ." +msgstr[1] "Giới hạn {min}-{max} từ." + +#: freetextresponse/freetextresponse.py:449 +msgid "Invalid Word Count. {word_count_message}" +msgstr "Số từ không hợp lệ. {word_count_message}" + +#: freetextresponse/freetextresponse.py:502 +msgid "{weight} point possible" +msgid_plural "{weight} points possible" +msgstr[0] "" +msgstr[1] "" + +#: freetextresponse/freetextresponse.py:515 +msgid "{score_string}/{weight} point" +msgid_plural "{score_string}/{weight} points" +msgstr[0] "" +msgstr[1] "" + +#: freetextresponse/freetextresponse.py:577 +msgid "You have used {count_attempts} of {max_attempts} submission" +msgid_plural "You have used {count_attempts} of {max_attempts} submissions" +msgstr[0] "Bạn đã thử {count_attempts} của {max_attempts} lần" +msgstr[1] "Bạn đã thử {count_attempts} của {max_attempts} lần" diff --git a/freetextresponse/private/view.html b/freetextresponse/private/view.html deleted file mode 100644 index 149d11bf..00000000 --- a/freetextresponse/private/view.html +++ /dev/null @@ -1,28 +0,0 @@ -
-

{self.display_name}

-
{problem_progress}
-

{self.prompt}

-
{word_count_message}
-
-
- - -
-
-
- {submitted_message} -
-
- - -
{used_attempts_feedback}
-
-
- {user_alert} -
-
- diff --git a/freetextresponse/private/view.js b/freetextresponse/private/view.js index 915d9d73..a51c8f2f 100644 --- a/freetextresponse/private/view.js +++ b/freetextresponse/private/view.js @@ -28,7 +28,7 @@ function FreeTextResponseView(runtime, element) { } buttonSubmit.on('click', function () { - buttonSubmit.text('Checking...'); + buttonSubmit.text(buttonSubmit[0].dataset.checking); runtime.notify('submit', { message: 'Submitting...', state: 'start' @@ -43,7 +43,7 @@ function FreeTextResponseView(runtime, element) { buttonSubmit.addClass(response.nodisplay_class); problemProgress.text(response.problem_progress); submissionReceivedMessage.text(response.submitted_message); - buttonSubmit.text('Submit'); + buttonSubmit.text(buttonSubmit[0].dataset.value); userAlertMessage.text(response.user_alert); buttonSave.addClass(response.nodisplay_class); setClassForTextAreaParent(response.indicator_class); @@ -60,7 +60,7 @@ function FreeTextResponseView(runtime, element) { }); buttonSave.on('click', function () { - buttonSave.text('Checking...'); + buttonSave.text(buttonSave[0].dataset.checking); runtime.notify('save', { message: 'Saving...', state: 'start' @@ -76,7 +76,7 @@ function FreeTextResponseView(runtime, element) { usedAttemptsFeedback.text(response.used_attempts_feedback); problemProgress.text(response.problem_progress); submissionReceivedMessage.text(response.submitted_message); - buttonSave.text('Save'); + buttonSave.text(buttonSave[0].dataset.value); userAlertMessage.text(response.user_alert); runtime.notify('save', { diff --git a/freetextresponse/public/view.html b/freetextresponse/public/view.html deleted file mode 100644 index d7a0cc9a..00000000 --- a/freetextresponse/public/view.html +++ /dev/null @@ -1 +0,0 @@ -

{self.display_name}

{problem_progress}

{self.prompt}

{word_count_message}
{submitted_message}
{used_attempts_feedback}
{user_alert}
\ No newline at end of file diff --git a/freetextresponse/public/view.js b/freetextresponse/public/view.js index 915d9d73..a51c8f2f 100644 --- a/freetextresponse/public/view.js +++ b/freetextresponse/public/view.js @@ -28,7 +28,7 @@ function FreeTextResponseView(runtime, element) { } buttonSubmit.on('click', function () { - buttonSubmit.text('Checking...'); + buttonSubmit.text(buttonSubmit[0].dataset.checking); runtime.notify('submit', { message: 'Submitting...', state: 'start' @@ -43,7 +43,7 @@ function FreeTextResponseView(runtime, element) { buttonSubmit.addClass(response.nodisplay_class); problemProgress.text(response.problem_progress); submissionReceivedMessage.text(response.submitted_message); - buttonSubmit.text('Submit'); + buttonSubmit.text(buttonSubmit[0].dataset.value); userAlertMessage.text(response.user_alert); buttonSave.addClass(response.nodisplay_class); setClassForTextAreaParent(response.indicator_class); @@ -60,7 +60,7 @@ function FreeTextResponseView(runtime, element) { }); buttonSave.on('click', function () { - buttonSave.text('Checking...'); + buttonSave.text(buttonSave[0].dataset.checking); runtime.notify('save', { message: 'Saving...', state: 'start' @@ -76,7 +76,7 @@ function FreeTextResponseView(runtime, element) { usedAttemptsFeedback.text(response.used_attempts_feedback); problemProgress.text(response.problem_progress); submissionReceivedMessage.text(response.submitted_message); - buttonSave.text('Save'); + buttonSave.text(buttonSave[0].dataset.value); userAlertMessage.text(response.user_alert); runtime.notify('save', { diff --git a/freetextresponse/public/view.js.min.js b/freetextresponse/public/view.js.min.js index bc45d02d..c6b1a675 100644 --- a/freetextresponse/public/view.js.min.js +++ b/freetextresponse/public/view.js.min.js @@ -1,2 +1,2 @@ -function FreeTextResponseView(a,b){"use strict";function c(a){m.removeClass("correct"),m.removeClass("incorrect"),m.removeClass("unanswered"),m.addClass(a)}var d=window.jQuery,e=d(b),f=e.find(".check.Submit"),g=e.find(".save"),h=e.find(".action .used-attempts-feedback"),i=e.find(".problem-progress"),j=e.find(".submission-received"),k=e.find(".user_alert"),l=e.find(".student_answer"),m=l.parent(),n=a.handlerUrl(b,"submit"),o=a.handlerUrl(b,"save_reponse");a.notify=a.notify||function(){console.log("POLYFILL runtime.notify",arguments)},f.on("click",function(){return f.text("Checking..."),a.notify("submit",{message:"Submitting...",state:"start"}),d.ajax(n,{type:"POST",data:JSON.stringify({student_answer:e.find(".student_answer").val()}),success:function(b){h.text(b.used_attempts_feedback),f.addClass(b.nodisplay_class),i.text(b.problem_progress),j.text(b.submitted_message),f.text("Submit"),k.text(b.user_alert),g.addClass(b.nodisplay_class),c(b.indicator_class),a.notify("submit",{state:"end"})},error:function(){a.notify("error",{})}}),!1}),g.on("click",function(){return g.text("Checking..."),a.notify("save",{message:"Saving...",state:"start"}),d.ajax(o,{type:"POST",data:JSON.stringify({student_answer:e.find(".student_answer").val()}),success:function(b){f.addClass(b.nodisplay_class),g.addClass(b.nodisplay_class),h.text(b.used_attempts_feedback),i.text(b.problem_progress),j.text(b.submitted_message),g.text("Save"),k.text(b.user_alert),a.notify("save",{state:"end"})},error:function(){a.notify("error",{})}}),!1}),l.on("keydown",function(){j.text(""),k.text(""),c("unanswered")})} +function FreeTextResponseView(a,b){"use strict";function c(a){m.removeClass("correct"),m.removeClass("incorrect"),m.removeClass("unanswered"),m.addClass(a)}var d=window.jQuery,e=d(b),f=e.find(".check.Submit"),g=e.find(".save"),h=e.find(".action .used-attempts-feedback"),i=e.find(".problem-progress"),j=e.find(".submission-received"),k=e.find(".user_alert"),l=e.find(".student_answer"),m=l.parent(),n=a.handlerUrl(b,"submit"),o=a.handlerUrl(b,"save_reponse");a.notify=a.notify||function(){console.log("POLYFILL runtime.notify",arguments)},f.on("click",function(){return f.text(f[0].dataset.checking),a.notify("submit",{message:"Submitting...",state:"start"}),d.ajax(n,{type:"POST",data:JSON.stringify({student_answer:e.find(".student_answer").val()}),success:function(b){h.text(b.used_attempts_feedback),f.addClass(b.nodisplay_class),i.text(b.problem_progress),j.text(b.submitted_message),f.text(f[0].dataset.value),k.text(b.user_alert),g.addClass(b.nodisplay_class),c(b.indicator_class),a.notify("submit",{state:"end"})},error:function(){a.notify("error",{})}}),!1}),g.on("click",function(){return g.text(g[0].dataset.checking),a.notify("save",{message:"Saving...",state:"start"}),d.ajax(o,{type:"POST",data:JSON.stringify({student_answer:e.find(".student_answer").val()}),success:function(b){f.addClass(b.nodisplay_class),g.addClass(b.nodisplay_class),h.text(b.used_attempts_feedback),i.text(b.problem_progress),j.text(b.submitted_message),g.text(g[0].dataset.value),k.text(b.user_alert),a.notify("save",{state:"end"})},error:function(){a.notify("error",{})}}),!1}),l.on("keydown",function(){j.text(""),k.text(""),c("unanswered")})} //# sourceMappingURL=view.js.min.js.map diff --git a/freetextresponse/public/view.js.min.js.map b/freetextresponse/public/view.js.min.js.map index 776e08ac..72dbb27e 100644 --- a/freetextresponse/public/view.js.min.js.map +++ b/freetextresponse/public/view.js.min.js.map @@ -1 +1 @@ -{"version":3,"file":"view.js.min.js","sources":["view.js"],"names":["FreeTextResponseView","runtime","element","setClassForTextAreaParent","new_class","textareaParent","removeClass","addClass","$","window","jQuery","$element","buttonSubmit","find","buttonSave","usedAttemptsFeedback","problemProgress","submissionReceivedMessage","userAlertMessage","textareaStudentAnswer","parent","url","handlerUrl","urlSave","notify","console","log","arguments","on","text","message","state","ajax","type","data","JSON","stringify","student_answer","val","success","response","used_attempts_feedback","nodisplay_class","problem_progress","submitted_message","user_alert","indicator_class","error"],"mappings":"AAAA,QAASA,sBAAqBC,EAASC,GACnC,YAqBA,SAASC,GAA0BC,GAC/BC,EAAeC,YAAY,WAC3BD,EAAeC,YAAY,aAC3BD,EAAeC,YAAY,cAC3BD,EAAeE,SAASH,GAvB5B,GAAII,GAAIC,OAAOC,OACXC,EAAWH,EAAEN,GACbU,EAAeD,EAASE,KAAK,iBAC7BC,EAAaH,EAASE,KAAK,SAC3BE,EAAuBJ,EAASE,KAAK,mCACrCG,EAAkBL,EAASE,KAAK,qBAChCI,EAA4BN,EAASE,KAAK,wBAC1CK,EAAmBP,EAASE,KAAK,eACjCM,EAAwBR,EAASE,KAAK,mBACtCR,EAAiBc,EAAsBC,SAEvCC,EAAMpB,EAAQqB,WAAWpB,EAAS,UAClCqB,EAAUtB,EAAQqB,WAAWpB,EAAS,eAG1CD,GAAQuB,OAASvB,EAAQuB,QAAU,WAC/BC,QAAQC,IAAI,0BAA2BC,YAU3Cf,EAAagB,GAAG,QAAS,WA6BrB,MA5BAhB,GAAaiB,KAAK,eAClB5B,EAAQuB,OAAO,UACXM,QAAS,gBACTC,MAAO,UAEXvB,EAAEwB,KAAKX,GACHY,KAAM,OACNC,KAAMC,KAAKC,WACPC,eAAkB1B,EAASE,KAAK,mBAAmByB,QAEvDC,QAAS,SAA+BC,GACpCzB,EAAqBc,KAAKW,EAASC,wBACnC7B,EAAaL,SAASiC,EAASE,iBAC/B1B,EAAgBa,KAAKW,EAASG,kBAC9B1B,EAA0BY,KAAKW,EAASI,mBACxChC,EAAaiB,KAAK,UAClBX,EAAiBW,KAAKW,EAASK,YAC/B/B,EAAWP,SAASiC,EAASE,iBAC7BvC,EAA0BqC,EAASM,iBAEnC7C,EAAQuB,OAAO,UACXO,MAAO,SAGfgB,MAAO,WACH9C,EAAQuB,OAAO,gBAGhB,IAGXV,EAAWc,GAAG,QAAS,WA4BnB,MA3BAd,GAAWe,KAAK,eAChB5B,EAAQuB,OAAO,QACXM,QAAS,YACTC,MAAO,UAEXvB,EAAEwB,KAAKT,GACHU,KAAM,OACNC,KAAMC,KAAKC,WACPC,eAAkB1B,EAASE,KAAK,mBAAmByB,QAEvDC,QAAS,SAA6BC,GAClC5B,EAAaL,SAASiC,EAASE,iBAC/B5B,EAAWP,SAASiC,EAASE,iBAC7B3B,EAAqBc,KAAKW,EAASC,wBACnCzB,EAAgBa,KAAKW,EAASG,kBAC9B1B,EAA0BY,KAAKW,EAASI,mBACxC9B,EAAWe,KAAK,QAChBX,EAAiBW,KAAKW,EAASK,YAE/B5C,EAAQuB,OAAO,QACXO,MAAO,SAGfgB,MAAO,WACH9C,EAAQuB,OAAO,gBAGhB,IAGXL,EAAsBS,GAAG,UAAW,WAEhCX,EAA0BY,KAAK,IAC/BX,EAAiBW,KAAK,IACtB1B,EAA0B"} \ No newline at end of file +{"version":3,"file":"view.js.min.js","sources":["view.js"],"names":["FreeTextResponseView","runtime","element","setClassForTextAreaParent","new_class","textareaParent","removeClass","addClass","$","window","jQuery","$element","buttonSubmit","find","buttonSave","usedAttemptsFeedback","problemProgress","submissionReceivedMessage","userAlertMessage","textareaStudentAnswer","parent","url","handlerUrl","urlSave","notify","console","log","arguments","on","text","dataset","checking","message","state","ajax","type","data","JSON","stringify","student_answer","val","success","response","used_attempts_feedback","nodisplay_class","problem_progress","submitted_message","value","user_alert","indicator_class","error"],"mappings":"AAAA,QAASA,sBAAqBC,EAASC,GACnC,YAqBA,SAASC,GAA0BC,GAC/BC,EAAeC,YAAY,WAC3BD,EAAeC,YAAY,aAC3BD,EAAeC,YAAY,cAC3BD,EAAeE,SAASH,GAvB5B,GAAII,GAAIC,OAAOC,OACXC,EAAWH,EAAEN,GACbU,EAAeD,EAASE,KAAK,iBAC7BC,EAAaH,EAASE,KAAK,SAC3BE,EAAuBJ,EAASE,KAAK,mCACrCG,EAAkBL,EAASE,KAAK,qBAChCI,EAA4BN,EAASE,KAAK,wBAC1CK,EAAmBP,EAASE,KAAK,eACjCM,EAAwBR,EAASE,KAAK,mBACtCR,EAAiBc,EAAsBC,SAEvCC,EAAMpB,EAAQqB,WAAWpB,EAAS,UAClCqB,EAAUtB,EAAQqB,WAAWpB,EAAS,eAG1CD,GAAQuB,OAASvB,EAAQuB,QAAU,WAC/BC,QAAQC,IAAI,0BAA2BC,YAU3Cf,EAAagB,GAAG,QAAS,WA6BrB,MA5BAhB,GAAaiB,KAAKjB,EAAa,GAAGkB,QAAQC,UAC1C9B,EAAQuB,OAAO,UACXQ,QAAS,gBACTC,MAAO,UAEXzB,EAAE0B,KAAKb,GACHc,KAAM,OACNC,KAAMC,KAAKC,WACPC,eAAkB5B,EAASE,KAAK,mBAAmB2B,QAEvDC,QAAS,SAA+BC,GACpC3B,EAAqBc,KAAKa,EAASC,wBACnC/B,EAAaL,SAASmC,EAASE,iBAC/B5B,EAAgBa,KAAKa,EAASG,kBAC9B5B,EAA0BY,KAAKa,EAASI,mBACxClC,EAAaiB,KAAKjB,EAAa,GAAGkB,QAAQiB,OAC1C7B,EAAiBW,KAAKa,EAASM,YAC/BlC,EAAWP,SAASmC,EAASE,iBAC7BzC,EAA0BuC,EAASO,iBAEnChD,EAAQuB,OAAO,UACXS,MAAO,SAGfiB,MAAO,WACHjD,EAAQuB,OAAO,gBAGhB,IAGXV,EAAWc,GAAG,QAAS,WA4BnB,MA3BAd,GAAWe,KAAKf,EAAW,GAAGgB,QAAQC,UACtC9B,EAAQuB,OAAO,QACXQ,QAAS,YACTC,MAAO,UAEXzB,EAAE0B,KAAKX,GACHY,KAAM,OACNC,KAAMC,KAAKC,WACPC,eAAkB5B,EAASE,KAAK,mBAAmB2B,QAEvDC,QAAS,SAA6BC,GAClC9B,EAAaL,SAASmC,EAASE,iBAC/B9B,EAAWP,SAASmC,EAASE,iBAC7B7B,EAAqBc,KAAKa,EAASC,wBACnC3B,EAAgBa,KAAKa,EAASG,kBAC9B5B,EAA0BY,KAAKa,EAASI,mBACxChC,EAAWe,KAAKf,EAAW,GAAGgB,QAAQiB,OACtC7B,EAAiBW,KAAKa,EAASM,YAE/B/C,EAAQuB,OAAO,QACXS,MAAO,SAGfiB,MAAO,WACHjD,EAAQuB,OAAO,gBAGhB,IAGXL,EAAsBS,GAAG,UAAW,WAEhCX,EAA0BY,KAAK,IAC/BX,EAAiBW,KAAK,IACtB1B,EAA0B"} \ No newline at end of file diff --git a/freetextresponse/settings.py b/freetextresponse/settings.py index 135c1175..665e598e 100644 --- a/freetextresponse/settings.py +++ b/freetextresponse/settings.py @@ -13,6 +13,7 @@ INSTALLED_APPS = ( 'django_nose', + 'freetextresponse', ) SECRET_KEY = 'freetextresponse_SECRET_KEY' diff --git a/freetextresponse/templates/freetextresponse_view.html b/freetextresponse/templates/freetextresponse_view.html new file mode 100644 index 00000000..07b75802 --- /dev/null +++ b/freetextresponse/templates/freetextresponse_view.html @@ -0,0 +1,24 @@ +{% load i18n %} +
+

{{ display_name }}

+
{{ problem_progress }}
+

{{ prompt }}

+
{{ word_count_message }}
+
+
+ + +
+
+
{{ submitted_message }}
+
+ + +
{{ used_attempts_feedback }}
+
+
{{ user_alert }}
+
diff --git a/freetextresponse/tests.py b/freetextresponse/tests.py index cd5326ab..c2efbbe7 100644 --- a/freetextresponse/tests.py +++ b/freetextresponse/tests.py @@ -118,103 +118,6 @@ def test_validate_field_data(self, **test_dict): validation_list[0].text, ) - def test_get_resource_string(self): - # pylint: disable=protected-access - """ - Checks that get_resource_string returns the proper html - """ - student_view_html = self.xblock.student_view().content - test_result = FreeTextResponse.get_resource_string('view.html') - test_result = test_result.format( - self=self.xblock, - word_count_message=self.xblock._get_word_count_message(), - indicator_class=self.xblock._get_indicator_class(), - problem_progress=self.xblock._get_problem_progress(), - used_attempts_feedback=self.xblock._get_used_attempts_feedback(), - nodisplay_class=self.xblock._get_nodisplay_class(), - visibility_class=self.xblock._get_indicator_visibility_class(), - submitted_message='', - user_alert='', - ) - self.assertEquals(student_view_html, test_result) - - @ddt.data('view.js.min.js', 'view.less.min.css') - def test_get_resource_url(self, path): - """ - Checks that get_resource_url the correct url - """ - public_path = '/resource/freetextresponse/public' - result = "{public_path}/{path}".format( - public_path=public_path, - path=path, - ) - self.xblock.runtime.local_resource_url = MagicMock( - return_value=result - ) - test_result = self.xblock.get_resource_url(path) - self.assertEquals(result, test_result) - - def test_build_fragment(self): - # pylint: disable=protected-access - """ - Checks build_fragment content and added resources - """ - # Get content - student_view = self.xblock.student_view() - student_view_html = student_view.content - # Build fragment - view_html = FreeTextResponse.get_resource_string('view.html') - view_html = view_html.format( - self=self.xblock, - word_count_message=self.xblock._get_word_count_message(), - indicator_class=self.xblock._get_indicator_class(), - problem_progress=self.xblock._get_problem_progress(), - used_attempts_feedback=self.xblock._get_used_attempts_feedback(), - nodisplay_class=self.xblock._get_nodisplay_class(), - visibility_class=self.xblock._get_indicator_visibility_class(), - submitted_message='', - user_alert='', - ) - public_path = '/resource/freetextresponse/public' - css_path = "{public_path}/{path}".format( - public_path=public_path, - path='view.less.min.css', - ) - path_js = "{public_path}/{path}".format( - public_path=public_path, - path='view.js.min.js', - ) - css_url = "{public_path}/{path}".format( - public_path=public_path, - path='some.fake.css', - ) - js_url = "{public_path}/{path}".format( - public_path=public_path, - path='some.fake.js', - ) - self.xblock.runtime.local_resource_url = MagicMock() - # Called once for each path - self.xblock.runtime.local_resource_url.side_effect = [ - css_path, - path_js - ] - test_result = self.xblock.build_fragment( - html_source=view_html, - paths_css=[css_path], - paths_js=[path_js], - urls_css=[css_url], - urls_js=[js_url], - fragment_js='FreeTextResponseView', - ) - self.assertEqual(student_view_html, test_result.content) - test_result_resource_names = [] - for resource in test_result.resources: - test_result_resource_names.append(resource.data) - self.assertIn(css_path, test_result_resource_names) - self.assertIn(path_js, test_result_resource_names) - self.assertIn(css_url, test_result_resource_names) - self.assertIn(js_url, test_result_resource_names) - def test_initialization_variables(self): """ Checks that instance variables are initialized correctly diff --git a/package.json b/package.json new file mode 100644 index 00000000..8818268a --- /dev/null +++ b/package.json @@ -0,0 +1,38 @@ +{ + "name": "xblock-free-text-response", + "title": "FreeTextResponse XBlock", + "description": "Enables instructors to create questions with free-text responses.", + "version": "0.1.1", + "homepage": "https://github.com/Stanford-Online/xblock-free-text-response", + "author": { + "name": "Azim Pradhan", + "email": "azim.pradhan@gmail.com" + }, + "repository": { + "type": "git", + "url": "https://github.com/Stanford-Online/xblock-free-text-response.git" + }, + "bugs": { + "url": "https://github.com/Stanford-Online/xblock-free-text-response/issues" + }, + "scripts": { + "test": "grunt --verbose" + }, + "devDependencies": { + "grunt": "^0.4.5", + "grunt-contrib-jshint": "^0.10.0", + "grunt-contrib-concat": "^0.5.0", + "grunt-contrib-uglify": "^0.6.0", + "grunt-contrib-less": "^0.11.4", + "grunt-contrib-csslint": "^0.3.1", + "grunt-contrib-cssmin": "^0.10.0", + "grunt-contrib-watch": "^0.6.1", + "grunt-contrib-copy": "^0.6.0", + "grunt-contrib-clean": "^0.6.0", + "grunt-contrib-htmlmin": "^0.3.0" + }, + "keywords": [ + "openedx", + "xblock" + ] +}