@@ -114,7 +113,7 @@ from contentstore import utils
A brief description of your education, experience, and expertise
-
+
@@ -143,7 +142,7 @@ from contentstore import utils
-
+
randomize all problems
@@ -217,7 +216,7 @@ from contentstore import utils
-
+
randomize all problems
@@ -283,7 +282,7 @@ from contentstore import utils
Discussions
-
+
General Settings
@@ -296,7 +295,7 @@ from contentstore import utils
-
+
Students and faculty will be able to post anonymously
@@ -320,7 +319,7 @@ from contentstore import utils
-
+
Students and faculty will be able to post anonymously
@@ -329,7 +328,7 @@ from contentstore import utils
-
+
This option is disabled since there are previous discussions that are anonymous.
@@ -351,7 +350,7 @@ from contentstore import utils
-
+
[prompt]')
+
+ describe 'insertRubric', ->
+ it 'inserts the template if selection is empty', ->
+ revisedSelection = OpenEndedMarkdownEditingDescriptor.insertRubric('')
+ expect(revisedSelection).toEqual(OpenEndedMarkdownEditingDescriptor.rubricTemplate)
+ it 'recognizes a proper rubric', ->
+ revisedSelection = OpenEndedMarkdownEditingDescriptor.insertRubric('[rubric]\n+1\n-1\n-2\n[rubric]')
+ expect(revisedSelection).toEqual('[rubric]\n+1\n-1\n-2\n[rubric]')
+
+ describe 'insertTasks', ->
+ it 'inserts the template if selection is empty', ->
+ revisedSelection = OpenEndedMarkdownEditingDescriptor.insertTasks('')
+ expect(revisedSelection).toEqual(OpenEndedMarkdownEditingDescriptor.tasksTemplate)
+ it 'recognizes a proper task string', ->
+ revisedSelection = OpenEndedMarkdownEditingDescriptor.insertTasks('[tasks](Self)[tasks]')
+ expect(revisedSelection).toEqual('[tasks](Self)[tasks]')
+
+ describe 'markdownToXml', ->
+ # test default templates
+ it 'converts prompt to xml', ->
+ data = OpenEndedMarkdownEditingDescriptor.markdownToXml("""[prompt]
+
Prompt!
+ This is my super awesome prompt.
+ [prompt]
+ """)
+ data = data.replace(/[\t\n\s]/gmi,'')
+ expect(data).toEqual("""
+
+
+
Prompt!
+ This is my super awesome prompt.
+
+
+ """.replace(/[\t\n\s]/gmi,''))
+
+ it 'converts rubric to xml', ->
+ data = OpenEndedMarkdownEditingDescriptor.markdownToXml("""[rubric]
+ + 1
+ -1
+ -2
+ + 2
+ -1
+ -2
+ +3
+ -1
+ -2
+ -3
+ [rubric]
+ """)
+ data = data.replace(/[\t\n\s]/gmi,'')
+ expect(data).toEqual("""
+
+
+
+
+ 1
+
+
+
+
+ 2
+
+
+
+
+ 3
+
+
+
+
+
+
+
+ """.replace(/[\t\n\s]/gmi,''))
+
+ it 'converts tasks to xml', ->
+ data = OpenEndedMarkdownEditingDescriptor.markdownToXml("""[tasks]
+ (Self), ({1-2}AI), ({1-4}AI), ({1-2}Peer
+ [tasks]
+ """)
+ data = data.replace(/[\t\n\s]/gmi,'')
+ equality_list = """
+
+
+
+
+
+ ml_grading.conf
+
+
+ ml_grading.conf
+
+
+ peer_grading.conf
+
+
+ """
+ expect(data).toEqual(equality_list.replace(/[\t\n\s]/gmi,''))
diff --git a/common/lib/xmodule/xmodule/js/src/combinedopenended/edit.coffee b/common/lib/xmodule/xmodule/js/src/combinedopenended/edit.coffee
new file mode 100644
index 0000000000..1b7f9bb4fb
--- /dev/null
+++ b/common/lib/xmodule/xmodule/js/src/combinedopenended/edit.coffee
@@ -0,0 +1,282 @@
+class @OpenEndedMarkdownEditingDescriptor extends XModule.Descriptor
+ # TODO really, these templates should come from or also feed the cheatsheet
+ @rubricTemplate : """
+ [rubric]
+ + Ideas
+ - Difficult for the reader to discern the main idea. Too brief or too repetitive to establish or maintain a focus.
+ - Attempts a main idea. Sometimes loses focus or ineffectively displays focus.
+ - Presents a unifying theme or main idea, but may include minor tangents. Stays somewhat focused on topic and task.
+ - Presents a unifying theme or main idea without going off on tangents. Stays completely focused on topic and task.
+ + Content
+ - Includes little information with few or no details or unrelated details. Unsuccessful in attempts to explore any facets of the topic.
+ - Includes little information and few or no details. Explores only one or two facets of the topic.
+ - Includes sufficient information and supporting details. (Details may not be fully developed; ideas may be listed.) Explores some facets of the topic.
+ - Includes in-depth information and exceptional supporting details that are fully developed. Explores all facets of the topic.
+ + Organization
+ - Ideas organized illogically, transitions weak, and response difficult to follow.
+ - Attempts to logically organize ideas. Attempts to progress in an order that enhances meaning, and demonstrates use of transitions.
+ - Ideas organized logically. Progresses in an order that enhances meaning. Includes smooth transitions.
+ + Style
+ - Contains limited vocabulary, with many words used incorrectly. Demonstrates problems with sentence patterns.
+ - Contains basic vocabulary, with words that are predictable and common. Contains mostly simple sentences (although there may be an attempt at more varied sentence patterns).
+ - Includes vocabulary to make explanations detailed and precise. Includes varied sentence patterns, including complex sentences.
+ + Voice
+ - Demonstrates language and tone that may be inappropriate to task and reader.
+ - Demonstrates an attempt to adjust language and tone to task and reader.
+ - Demonstrates effective adjustment of language and tone to task and reader.
+ [rubric]
+ """
+
+ @tasksTemplate: "[tasks]\n(Self), ({4-12}AI), ({9-12}Peer)\n[tasks]\n"
+ @promptTemplate: """
+ [prompt]\n
+
Censorship in the Libraries
+
+
'All of us can think of a book that we hope none of our children or any other children have taken off the shelf. But if I have the right to remove that book from the shelf -- that work I abhor -- then you also have exactly the same right and so does everyone else. And then we have no books left on the shelf for any of us.' --Katherine Paterson, Author
+
+
+
+Write a persuasive essay to a newspaper reflecting your vies on censorship in libraries. Do you believe that certain materials, such as books, music, movies, magazines, etc., should be removed from the shelves if they are found offensive? Support your position with convincing arguments from your own experience, observations, and/or reading.
+
+ [prompt]\n
+ """
+
+ constructor: (element) ->
+ @element = element
+
+ if $(".markdown-box", @element).length != 0
+ @markdown_editor = CodeMirror.fromTextArea($(".markdown-box", element)[0], {
+ lineWrapping: true
+ mode: null
+ })
+ @setCurrentEditor(@markdown_editor)
+ # Add listeners for toolbar buttons (only present for markdown editor)
+ @element.on('click', '.xml-tab', @onShowXMLButton)
+ @element.on('click', '.format-buttons a', @onToolbarButton)
+ @element.on('click', '.cheatsheet-toggle', @toggleCheatsheet)
+ # Hide the XML text area
+ $(@element.find('.xml-box')).hide()
+ else
+ @createXMLEditor()
+
+ ###
+ Creates the XML Editor and sets it as the current editor. If text is passed in,
+ it will replace the text present in the HTML template.
+
+ text: optional argument to override the text passed in via the HTML template
+ ###
+ createXMLEditor: (text) ->
+ @xml_editor = CodeMirror.fromTextArea($(".xml-box", @element)[0], {
+ mode: "xml"
+ lineNumbers: true
+ lineWrapping: true
+ })
+ if text
+ @xml_editor.setValue(text)
+ @setCurrentEditor(@xml_editor)
+
+ ###
+ User has clicked to show the XML editor. Before XML editor is swapped in,
+ the user will need to confirm the one-way conversion.
+ ###
+ onShowXMLButton: (e) =>
+ e.preventDefault();
+ if @confirmConversionToXml()
+ @createXMLEditor(OpenEndedMarkdownEditingDescriptor.markdownToXml(@markdown_editor.getValue()))
+ # Need to refresh to get line numbers to display properly (and put cursor position to 0)
+ @xml_editor.setCursor(0)
+ @xml_editor.refresh()
+ # Hide markdown-specific toolbar buttons
+ $(@element.find('.editor-bar')).hide()
+
+ ###
+ Have the user confirm the one-way conversion to XML.
+ Returns true if the user clicked OK, else false.
+ ###
+ confirmConversionToXml: ->
+ # TODO: use something besides a JavaScript confirm dialog?
+ return confirm("If you use the Advanced Editor, this problem will be converted to XML and you will not be able to return to the Simple Editor Interface.\n\nProceed to the Advanced Editor and convert this problem to XML?")
+
+ ###
+ Event listener for toolbar buttons (only possible when markdown editor is visible).
+ ###
+ onToolbarButton: (e) =>
+ e.preventDefault();
+ selection = @markdown_editor.getSelection()
+ revisedSelection = null
+ switch $(e.currentTarget).attr('class')
+ when "rubric-button" then revisedSelection = OpenEndedMarkdownEditingDescriptor.insertRubric(selection)
+ when "prompt-button" then revisedSelection = OpenEndedMarkdownEditingDescriptor.insertPrompt(selection)
+ when "tasks-button" then revisedSelection = OpenEndedMarkdownEditingDescriptor.insertTasks(selection)
+ else # ignore click
+
+ if revisedSelection != null
+ @markdown_editor.replaceSelection(revisedSelection)
+ @markdown_editor.focus()
+
+ ###
+ Event listener for toggling cheatsheet (only possible when markdown editor is visible).
+ ###
+ toggleCheatsheet: (e) =>
+ e.preventDefault();
+ if !$(@markdown_editor.getWrapperElement()).find('.simple-editor-open-ended-cheatsheet')[0]
+ @cheatsheet = $($('#simple-editor-open-ended-cheatsheet').html())
+ $(@markdown_editor.getWrapperElement()).append(@cheatsheet)
+
+ setTimeout (=> @cheatsheet.toggleClass('shown')), 10
+
+ ###
+ Stores the current editor and hides the one that is not displayed.
+ ###
+ setCurrentEditor: (editor) ->
+ if @current_editor
+ $(@current_editor.getWrapperElement()).hide()
+ @current_editor = editor
+ $(@current_editor.getWrapperElement()).show()
+ $(@current_editor).focus();
+
+ ###
+ Called when save is called. Listeners are unregistered because editing the block again will
+ result in a new instance of the descriptor. Note that this is NOT the case for cancel--
+ when cancel is called the instance of the descriptor is reused if edit is selected again.
+ ###
+ save: ->
+ @element.off('click', '.xml-tab', @changeEditor)
+ @element.off('click', '.format-buttons a', @onToolbarButton)
+ @element.off('click', '.cheatsheet-toggle', @toggleCheatsheet)
+ if @current_editor == @markdown_editor
+ {
+ data: OpenEndedMarkdownEditingDescriptor.markdownToXml(@markdown_editor.getValue())
+ metadata:
+ markdown: @markdown_editor.getValue()
+ }
+ else
+ {
+ data: @xml_editor.getValue()
+ metadata:
+ markdown: null
+ }
+
+ @insertRubric: (selectedText) ->
+ return OpenEndedMarkdownEditingDescriptor.insertGenericInput(selectedText, '[rubric]', '[rubric]', OpenEndedMarkdownEditingDescriptor.rubricTemplate)
+
+ @insertPrompt: (selectedText) ->
+ return OpenEndedMarkdownEditingDescriptor.insertGenericInput(selectedText, '[prompt]', '[prompt]', OpenEndedMarkdownEditingDescriptor.promptTemplate)
+
+ @insertTasks: (selectedText) ->
+ return OpenEndedMarkdownEditingDescriptor.insertGenericInput(selectedText, '[tasks]', '[tasks]', OpenEndedMarkdownEditingDescriptor.tasksTemplate)
+
+ @insertGenericInput: (selectedText, lineStart, lineEnd, template) ->
+ if selectedText.length > 0
+ new_string = selectedText.replace(/^\s+|\s+$/g,'')
+ if new_string.substring(0,lineStart.length) != lineStart
+ new_string = lineStart + new_string
+ if new_string.substring((new_string.length)-lineEnd.length,new_string.length) != lineEnd
+ new_string = new_string + lineEnd
+ return new_string
+ else
+ return template
+
+ @markdownToXml: (markdown)->
+ toXml = `function(markdown) {
+
+ function template(template_html,data){
+ return template_html.replace(/%(\w*)%/g,function(m,key){return data.hasOwnProperty(key)?data[key]:"";});
+ }
+
+ var xml = markdown;
+
+ // group rubrics
+ xml = xml.replace(/\[rubric\]\n?([^\]]*)\[\/?rubric\]/gmi, function(match, p) {
+ var groupString = '\n\n';
+ var options = p.split('\n');
+ var category_open = false;
+ for(var i = 0; i < options.length; i++) {
+ if(options[i].length > 0) {
+ var value = options[i].replace(/^\s+|\s+$/g,'');
+ if (value.charAt(0)=="+") {
+ if(i>0){
+ if(category_open==true){
+ groupString += "\n";
+ category_open = false;
+ }
+ }
+ groupString += "\n\n";
+ category_open = true;
+ text = value.substr(1);
+ text = text.replace(/^\s+|\s+$/g,'');
+ groupString += text;
+ groupString += "\n\n";
+ } else if (value.charAt(0) == "-") {
+ groupString += "\n";
+ }
+ }
+ if(i==options.length-1 && category_open == true){
+ groupString += "\n\n";
+ }
+ }
+ groupString += '\n\n';
+ return groupString;
+ });
+
+ // group tasks
+ xml = xml.replace(/\[tasks\]\n?([^\]]*)\[\/?tasks\]/gmi, function(match, p) {
+ var open_ended_template = $('#open-ended-template').html();
+ if(open_ended_template == null) {
+ open_ended_template = "%grading_config%";
+ }
+ var groupString = '';
+ var options = p.split(",");
+ for(var i = 0; i < options.length; i++) {
+ if(options[i].length > 0) {
+ var value = options[i].replace(/^\s+|\s+$/g,'');
+ var lower_option = value.toLowerCase();
+ type = lower_option.match(/(peer|self|ai)/gmi)
+ if(type != null) {
+ type = type[0]
+ var min_max = value.match(/\{\n?([^\]]*)\}/gmi);
+ var min_max_string = "";
+ if(min_max!=null) {
+ min_max = min_max[0].replace(/^{|}/gmi,'');
+ min_max = min_max.split("-");
+ min = min_max[0];
+ max = min_max[1];
+ min_max_string = 'min_score_to_attempt="' + min + '" max_score_to_attempt="' + max + '" ';
+ }
+ groupString += "\n"
+ if(type=="self") {
+ groupString +=""
+ } else if (type=="peer") {
+ config = "peer_grading.conf"
+ groupString += template(open_ended_template,{min_max_string: min_max_string, grading_config: config});
+ } else if (type=="ai") {
+ config = "ml_grading.conf"
+ groupString += template(open_ended_template,{min_max_string: min_max_string, grading_config: config});
+ }
+ groupString += "\n"
+ }
+ }
+ }
+ return groupString;
+ });
+
+ // replace prompts
+ xml = xml.replace(/\[prompt\]\n?([^\]]*)\[\/?prompt\]/gmi, function(match, p1) {
+ var selectString = '\n' + p1 + '\n';
+ return selectString;
+ });
+
+ // rid white space
+ xml = xml.replace(/\n\n\n/g, '\n');
+
+ // surround w/ combinedopenended tag
+ xml = '\n' + xml + '\n';
+
+ return xml;
+ }
+ `
+ return toXml markdown
diff --git a/common/lib/xmodule/xmodule/modulestore/__init__.py b/common/lib/xmodule/xmodule/modulestore/__init__.py
index ae04e3aac4..33c7b61251 100644
--- a/common/lib/xmodule/xmodule/modulestore/__init__.py
+++ b/common/lib/xmodule/xmodule/modulestore/__init__.py
@@ -9,7 +9,7 @@ import re
from collections import namedtuple
from .exceptions import InvalidLocationError, InsufficientSpecificationError
-from xmodule.errortracker import ErrorLog, make_error_tracker
+from xmodule.errortracker import make_error_tracker
from bson.son import SON
log = logging.getLogger('mitx.' + 'modulestore')
@@ -64,7 +64,6 @@ class Location(_LocationBase):
"""
return re.sub('_+', '_', invalid.sub('_', value))
-
@staticmethod
def clean(value):
"""
@@ -72,7 +71,6 @@ class Location(_LocationBase):
"""
return Location._clean(value, INVALID_CHARS)
-
@staticmethod
def clean_keeping_underscores(value):
"""
@@ -82,7 +80,6 @@ class Location(_LocationBase):
"""
return INVALID_CHARS.sub('_', value)
-
@staticmethod
def clean_for_url_name(value):
"""
@@ -154,9 +151,7 @@ class Location(_LocationBase):
to mean wildcard selection.
"""
-
- if (org is None and course is None and category is None and
- name is None and revision is None):
+ if (org is None and course is None and category is None and name is None and revision is None):
location = loc_or_tag
else:
location = (loc_or_tag, org, course, category, name, revision)
@@ -191,7 +186,7 @@ class Location(_LocationBase):
match = MISSING_SLASH_URL_RE.match(location)
if match is None:
log.debug('location is instance of %s but no URL match' % basestring)
- raise InvalidLocationError(location)
+ raise InvalidLocationError(location)
groups = match.groupdict()
check_dict(groups)
return _LocationBase.__new__(_cls, **groups)
@@ -233,7 +228,7 @@ class Location(_LocationBase):
html id attributes
"""
s = "-".join(str(v) for v in self.list()
- if v is not None)
+ if v is not None)
return Location.clean_for_html(s)
def dict(self):
@@ -258,6 +253,12 @@ class Location(_LocationBase):
at the location URL hierachy"""
return "/".join([self.org, self.course, self.name])
+ def replace(self, **kwargs):
+ '''
+ Expose a public method for replacing location elements
+ '''
+ return self._replace(**kwargs)
+
class ModuleStore(object):
"""
@@ -382,12 +383,6 @@ class ModuleStore(object):
'''
raise NotImplementedError
- def get_course(self, course_id):
- '''
- Look for a specific course id. Returns the course descriptor, or None if not found.
- '''
- raise NotImplementedError
-
def get_parent_locations(self, location, course_id):
'''Find all locations that are the parents of this location in this
course. Needed for path_to_location().
@@ -406,8 +401,7 @@ class ModuleStore(object):
courses = [
course
for course in self.get_courses()
- if course.location.org == location.org
- and course.location.course == location.course
+ if course.location.org == location.org and course.location.course == location.course
]
return courses
diff --git a/common/lib/xmodule/xmodule/modulestore/draft.py b/common/lib/xmodule/xmodule/modulestore/draft.py
index c3f1b23688..9262c5e9d6 100644
--- a/common/lib/xmodule/xmodule/modulestore/draft.py
+++ b/common/lib/xmodule/xmodule/modulestore/draft.py
@@ -13,11 +13,12 @@ def as_draft(location):
"""
return Location(location)._replace(revision=DRAFT)
+
def as_published(location):
"""
Returns the Location that is the published version for `location`
"""
- return Location(location)._replace(revision=None)
+ return Location(location)._replace(revision=None)
def wrap_draft(item):
diff --git a/common/lib/xmodule/xmodule/modulestore/tests/factories.py b/common/lib/xmodule/xmodule/modulestore/tests/factories.py
index 31237af7b9..8cf148f742 100644
--- a/common/lib/xmodule/xmodule/modulestore/tests/factories.py
+++ b/common/lib/xmodule/xmodule/modulestore/tests/factories.py
@@ -3,7 +3,6 @@ from time import gmtime
from uuid import uuid4
from xmodule.modulestore import Location
from xmodule.modulestore.django import modulestore
-from xmodule.timeparse import stringify_time
from xmodule.modulestore.inheritance import own_metadata
diff --git a/common/lib/xmodule/xmodule/open_ended_grading_classes/combined_open_ended_modulev1.py b/common/lib/xmodule/xmodule/open_ended_grading_classes/combined_open_ended_modulev1.py
index 1404f52300..e289ba72f1 100644
--- a/common/lib/xmodule/xmodule/open_ended_grading_classes/combined_open_ended_modulev1.py
+++ b/common/lib/xmodule/xmodule/open_ended_grading_classes/combined_open_ended_modulev1.py
@@ -847,8 +847,8 @@ class CombinedOpenEndedV1Descriptor():
if len(xml_object.xpath(child)) == 0:
#This is a staff_facing_error
raise ValueError(
- "Combined Open Ended definition must include at least one '{0}' tag. Contact the learning sciences group for assistance.".format(
- child))
+ "Combined Open Ended definition must include at least one '{0}' tag. Contact the learning sciences group for assistance. {1}".format(
+ child, xml_object))
def parse_task(k):
"""Assumes that xml_object has child k"""
diff --git a/common/lib/xmodule/xmodule/open_ended_grading_classes/grading_service_module.py b/common/lib/xmodule/xmodule/open_ended_grading_classes/grading_service_module.py
index b16f0618bb..3e3f943cd7 100644
--- a/common/lib/xmodule/xmodule/open_ended_grading_classes/grading_service_module.py
+++ b/common/lib/xmodule/xmodule/open_ended_grading_classes/grading_service_module.py
@@ -53,8 +53,9 @@ class GradingService(object):
except (RequestException, ConnectionError, HTTPError) as err:
# reraise as promised GradingServiceError, but preserve stacktrace.
#This is a dev_facing_error
- log.error("Problem posting data to the grading controller. URL: {0}, data: {1}".format(url, data))
- raise GradingServiceError, str(err), sys.exc_info()[2]
+ error_string = "Problem posting data to the grading controller. URL: {0}, data: {1}".format(url, data)
+ log.error(error_string)
+ raise GradingServiceError(error_string)
return r.text
@@ -71,8 +72,9 @@ class GradingService(object):
except (RequestException, ConnectionError, HTTPError) as err:
# reraise as promised GradingServiceError, but preserve stacktrace.
#This is a dev_facing_error
- log.error("Problem getting data from the grading controller. URL: {0}, params: {1}".format(url, params))
- raise GradingServiceError, str(err), sys.exc_info()[2]
+ error_string = "Problem getting data from the grading controller. URL: {0}, params: {1}".format(url, params)
+ log.error(error_string)
+ raise GradingServiceError(error_string)
return r.text
diff --git a/common/lib/xmodule/xmodule/open_ended_grading_classes/open_ended_module.py b/common/lib/xmodule/xmodule/open_ended_grading_classes/open_ended_module.py
index 7ba046b2ad..4f772fe0a1 100644
--- a/common/lib/xmodule/xmodule/open_ended_grading_classes/open_ended_module.py
+++ b/common/lib/xmodule/xmodule/open_ended_grading_classes/open_ended_module.py
@@ -168,7 +168,10 @@ class OpenEndedModule(openendedchild.OpenEndedChild):
#This is a student_facing_error
return {'success': False, 'msg': "There was an error saving your feedback. Please contact course staff."}
- qinterface = system.xqueue['interface']
+ xqueue = system.get('xqueue')
+ if xqueue is None:
+ return {'success': False, 'msg': "Couldn't submit feedback."}
+ qinterface = xqueue['interface']
qtime = datetime.strftime(datetime.now(), xqueue_interface.dateformat)
anonymous_student_id = system.anonymous_student_id
queuekey = xqueue_interface.make_hashkey(str(system.seed) + qtime +
@@ -176,7 +179,7 @@ class OpenEndedModule(openendedchild.OpenEndedChild):
str(len(self.child_history)))
xheader = xqueue_interface.make_xheader(
- lms_callback_url=system.xqueue['construct_callback'](),
+ lms_callback_url=xqueue['construct_callback'](),
lms_key=queuekey,
queue_name=self.message_queue_name
)
@@ -219,7 +222,10 @@ class OpenEndedModule(openendedchild.OpenEndedChild):
# Prepare xqueue request
#------------------------------------------------------------
- qinterface = system.xqueue['interface']
+ xqueue = system.get('xqueue')
+ if xqueue is None:
+ return False
+ qinterface = xqueue['interface']
qtime = datetime.strftime(datetime.now(), xqueue_interface.dateformat)
anonymous_student_id = system.anonymous_student_id
@@ -230,7 +236,7 @@ class OpenEndedModule(openendedchild.OpenEndedChild):
str(len(self.child_history)))
xheader = xqueue_interface.make_xheader(
- lms_callback_url=system.xqueue['construct_callback'](),
+ lms_callback_url=xqueue['construct_callback'](),
lms_key=queuekey,
queue_name=self.queue_name
)
diff --git a/common/lib/xmodule/xmodule/peer_grading_module.py b/common/lib/xmodule/xmodule/peer_grading_module.py
index eebfbe22e5..b0d666621f 100644
--- a/common/lib/xmodule/xmodule/peer_grading_module.py
+++ b/common/lib/xmodule/xmodule/peer_grading_module.py
@@ -587,3 +587,6 @@ class PeerGradingDescriptor(PeerGradingFields, RawDescriptor):
has_score = True
always_recalculate_grades = True
template_dir_name = "peer_grading"
+
+ #Specify whether or not to pass in open ended interface
+ needs_open_ended_interface = True
diff --git a/common/lib/xmodule/xmodule/randomize_module.py b/common/lib/xmodule/xmodule/randomize_module.py
index 240f33e33e..434706530b 100644
--- a/common/lib/xmodule/xmodule/randomize_module.py
+++ b/common/lib/xmodule/xmodule/randomize_module.py
@@ -4,6 +4,8 @@ import random
from xmodule.x_module import XModule
from xmodule.seq_module import SequenceDescriptor
+from lxml import etree
+
from xblock.core import Scope, Integer
log = logging.getLogger('mitx.' + __name__)
diff --git a/common/lib/xmodule/xmodule/templates/combinedopenended/default.yaml b/common/lib/xmodule/xmodule/templates/combinedopenended/default.yaml
index a11367b46f..d74517a3c3 100644
--- a/common/lib/xmodule/xmodule/templates/combinedopenended/default.yaml
+++ b/common/lib/xmodule/xmodule/templates/combinedopenended/default.yaml
@@ -7,6 +7,7 @@ metadata:
skip_spelling_checks: False
accept_file_upload: False
weight: ""
+ markdown: ""
data: |
@@ -39,5 +40,4 @@ data: |
-
children: []
diff --git a/common/lib/xmodule/xmodule/tests/test_combined_open_ended.py b/common/lib/xmodule/xmodule/tests/test_combined_open_ended.py
index 917e90e575..409347882f 100644
--- a/common/lib/xmodule/xmodule/tests/test_combined_open_ended.py
+++ b/common/lib/xmodule/xmodule/tests/test_combined_open_ended.py
@@ -407,7 +407,7 @@ class CombinedOpenEndedModuleTest(unittest.TestCase):
self.assertTrue(changed)
def test_get_max_score(self):
- changed = self.combinedoe.update_task_states()
+ self.combinedoe.update_task_states()
self.combinedoe.state = "done"
self.combinedoe.is_scored = True
max_score = self.combinedoe.max_score()
@@ -611,11 +611,11 @@ class OpenEndedModuleXmlTest(unittest.TestCase, DummyModulestore):
self.assertEqual(module.current_task_number, 1)
#Get html and other data client will request
- html = module.get_html()
+ module.get_html()
legend = module.handle_ajax("get_legend", {})
self.assertTrue(isinstance(legend, basestring))
- status = module.handle_ajax("get_status", {})
+ module.handle_ajax("get_status", {})
module.handle_ajax("skip_post_assessment", {})
self.assertTrue(isinstance(legend, basestring))
diff --git a/common/lib/xmodule/xmodule/xml_module.py b/common/lib/xmodule/xmodule/xml_module.py
index 7480cda0c5..2f54bbf405 100644
--- a/common/lib/xmodule/xmodule/xml_module.py
+++ b/common/lib/xmodule/xmodule/xml_module.py
@@ -136,6 +136,7 @@ class XmlDescriptor(XModuleDescriptor):
'hide_progress_tab': bool_map,
'allow_anonymous': bool_map,
'allow_anonymous_to_peers': bool_map,
+ 'show_timezone': bool_map,
}
diff --git a/common/templates/jasmine/base.html b/common/templates/jasmine/base.html
index 9a1b3bed92..0133edadfa 100644
--- a/common/templates/jasmine/base.html
+++ b/common/templates/jasmine/base.html
@@ -12,6 +12,7 @@
+
{% load compressed %}
{# static files #}
@@ -37,15 +38,14 @@
+
@@ -44,30 +45,10 @@
diff --git a/common/test/data/full/course.xml b/common/test/data/full/course.xml
index 7a05db42f2..b2f9097020 100644
--- a/common/test/data/full/course.xml
+++ b/common/test/data/full/course.xml
@@ -1 +1 @@
-
+
diff --git a/common/test/data/full/sequential/Administrivia_and_Circuit_Elements.xml b/common/test/data/full/sequential/Administrivia_and_Circuit_Elements.xml
index 26f8f5a08d..47b19f75ed 100644
--- a/common/test/data/full/sequential/Administrivia_and_Circuit_Elements.xml
+++ b/common/test/data/full/sequential/Administrivia_and_Circuit_Elements.xml
@@ -12,4 +12,13 @@
Minor correction: Six elements (five resistors)…
+
+
+
+
+
+
Inline content…
+
+
+
diff --git a/common/test/phantom-jasmine b/common/test/phantom-jasmine
deleted file mode 160000
index a54d435b55..0000000000
--- a/common/test/phantom-jasmine
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit a54d435b5556650efbcdb0490e6c7928ac75238a
diff --git a/doc/testing.md b/doc/testing.md
index d6c7b7ee86..e5d035d90e 100644
--- a/doc/testing.md
+++ b/doc/testing.md
@@ -8,7 +8,7 @@ and acceptance tests.
### Unit Tests
* Each test case should be concise: setup, execute, check, and teardown.
-If you find yourself writing tests with many steps, consider refactoring
+If you find yourself writing tests with many steps, consider refactoring
the unit under tests into smaller units, and then testing those individually.
* As a rule of thumb, your unit tests should cover every code branch.
@@ -16,19 +16,19 @@ the unit under tests into smaller units, and then testing those individually.
* Mock or patch external dependencies.
We use [voidspace mock](http://www.voidspace.org.uk/python/mock/).
-* We unit test Python code (using [unittest](http://docs.python.org/2/library/unittest.html)) and
+* We unit test Python code (using [unittest](http://docs.python.org/2/library/unittest.html)) and
Javascript (using [Jasmine](http://pivotal.github.io/jasmine/))
### Integration Tests
* Test several units at the same time.
Note that you can still mock or patch dependencies
-that are not under test! For example, you might test that
-`LoncapaProblem`, `NumericalResponse`, and `CorrectMap` in the
+that are not under test! For example, you might test that
+`LoncapaProblem`, `NumericalResponse`, and `CorrectMap` in the
`capa` package work together, while still mocking out template rendering.
* Use integration tests to ensure that units are hooked up correctly.
-You do not need to test every possible input--that's what unit
-tests are for. Instead, focus on testing the "happy path"
+You do not need to test every possible input--that's what unit
+tests are for. Instead, focus on testing the "happy path"
to verify that the components work together correctly.
* Many of our tests use the [Django test client](https://docs.djangoproject.com/en/dev/topics/testing/overview/) to simulate
@@ -43,8 +43,8 @@ these tests simulate user interactions through the browser using
Overall, you want to write the tests that **maximize coverage**
while **minimizing maintenance**.
-In practice, this usually means investing heavily
-in unit tests, which tend to be the most robust to changes in the code base.
+In practice, this usually means investing heavily
+in unit tests, which tend to be the most robust to changes in the code base.

@@ -53,13 +53,13 @@ and acceptance tests. Most of our tests are unit tests or integration tests.
## Test Locations
-* Python unit and integration tests: Located in
+* Python unit and integration tests: Located in
subpackages called `tests`.
-For example, the tests for the `capa` package are located in
+For example, the tests for the `capa` package are located in
`common/lib/capa/capa/tests`.
* Javascript unit tests: Located in `spec` folders. For example,
-`common/lib/xmodule/xmodule/js/spec` and `{cms,lms}/static/coffee/spec`
+`common/lib/xmodule/xmodule/js/spec` and `{cms,lms}/static/coffee/spec`
For consistency, you should use the same directory structure for implementation
and test. For example, the test for `src/views/module.coffee`
should be written in `spec/views/module_spec.coffee`.
@@ -88,7 +88,7 @@ because the `capa` package handles problem XML.
Before running tests, ensure that you have all the dependencies. You can install dependencies using:
- pip install -r requirements.txt
+ rake install_prereqs
## Running Python Unit tests
@@ -101,7 +101,7 @@ You can run tests using `rake` commands. For example,
rake test
-runs all the tests. It also runs `collectstatic`, which prepares the static files used by the site (for example, compiling Coffeescript to Javascript).
+runs all the tests. It also runs `collectstatic`, which prepares the static files used by the site (for example, compiling Coffeescript to Javascript).
You can also run the tests without `collectstatic`, which tends to be faster:
@@ -117,12 +117,11 @@ xmodule can be tested independently, with this:
To run a single django test class:
- django-admin.py test --settings=lms.envs.test --pythonpath=. lms/djangoapps/courseware/tests/tests.py:TestViewAuth
+ rake test_lms[courseware.tests.tests:testViewAuth]
To run a single django test:
- django-admin.py test --settings=lms.envs.test --pythonpath=. lms/djangoapps/courseware/tests/tests.py:TestViewAuth.test_dark_launch
-
+ rake test_lms[courseware.tests.tests:TestViewAuth.test_dark_launch]
To run a single nose test file:
@@ -150,7 +149,7 @@ If the `phantomjs` binary is not on the path, set the `PHANTOMJS_PATH` environme
PHANTOMJS_PATH=/path/to/phantomjs rake phantomjs_jasmine_{lms,cms}
-Once you have run the `rake` command, your browser should open to
+Once you have run the `rake` command, your browser should open to
to `http://localhost/_jasmine/`, which displays the test results.
**Troubleshooting**: If you get an error message while running the `rake` task,
@@ -163,7 +162,7 @@ Most of our tests use [Splinter](http://splinter.cobrateam.info/)
to simulate UI browser interactions. Splinter, in turn,
uses [Selenium](http://docs.seleniumhq.org/) to control the Chrome browser.
-**Prerequisite**: You must have [ChromeDriver](https://code.google.com/p/selenium/wiki/ChromeDriver)
+**Prerequisite**: You must have [ChromeDriver](https://code.google.com/p/selenium/wiki/ChromeDriver)
installed to run the tests in Chrome. The tests are confirmed to run
with Chrome (not Chromium) version 26.0.0.1410.63 with ChromeDriver
version r195636.
@@ -184,13 +183,7 @@ To start the debugger on failure, add the `--pdb` option:
To run tests faster by not collecting static files, you can use
`rake fasttest_acceptance_lms` and `rake fasttest_acceptance_cms`.
-
-**Troubleshooting**: If you get an error message that says something about harvest not being a command, you probably are missing a requirement.
-Try running:
-
- pip install -r requirements.txt
-
-**Note**: The acceptance tests can *not* currently run in parallel.
+**Note**: The acceptance tests can *not* currently run in parallel.
## Viewing Test Coverage
diff --git a/jenkins/test.sh b/jenkins/test.sh
index d8cd2c1843..35be3a0121 100755
--- a/jenkins/test.sh
+++ b/jenkins/test.sh
@@ -73,8 +73,8 @@ rake pylint > pylint.log || cat pylint.log
TESTS_FAILED=0
# Run the python unit tests
-rake test_cms[false] || TESTS_FAILED=1
-rake test_lms[false] || TESTS_FAILED=1
+rake test_cms || TESTS_FAILED=1
+rake test_lms || TESTS_FAILED=1
rake test_common/lib/capa || TESTS_FAILED=1
rake test_common/lib/xmodule || TESTS_FAILED=1
@@ -82,7 +82,7 @@ rake test_common/lib/xmodule || TESTS_FAILED=1
rake phantomjs_jasmine_lms || TESTS_FAILED=1
rake phantomjs_jasmine_cms || TESTS_FAILED=1
rake phantomjs_jasmine_common/lib/xmodule || TESTS_FAILED=1
-rake phantomjs_jasmine_discussion || TESTS_FAILED=1
+rake phantomjs_jasmine_common/static/coffee || TESTS_FAILED=1
rake coverage:xml coverage:html
diff --git a/lms/djangoapps/courseware/module_render.py b/lms/djangoapps/courseware/module_render.py
index 2a665cd8a0..284b746249 100644
--- a/lms/djangoapps/courseware/module_render.py
+++ b/lms/djangoapps/courseware/module_render.py
@@ -214,22 +214,27 @@ def get_module_for_descriptor(user, request, descriptor, model_data_cache, cours
#This is a hacky way to pass settings to the combined open ended xmodule
#It needs an S3 interface to upload images to S3
#It needs the open ended grading interface in order to get peer grading to be done
- #TODO: refactor these settings into module-specific settings when possible.
#this first checks to see if the descriptor is the correct one, and only sends settings if it is
- is_descriptor_combined_open_ended = (descriptor.__class__.__name__ == 'CombinedOpenEndedDescriptor')
- is_descriptor_peer_grading = (descriptor.__class__.__name__ == 'PeerGradingDescriptor')
+
+ #Get descriptor metadata fields indicating needs for various settings
+ needs_open_ended_interface = getattr(descriptor, "needs_open_ended_interface", False)
+ needs_s3_interface = getattr(descriptor, "needs_s3_interface", False)
+
+ #Initialize interfaces to None
open_ended_grading_interface = None
s3_interface = None
- if is_descriptor_combined_open_ended or is_descriptor_peer_grading:
+
+ #Create interfaces if needed
+ if needs_open_ended_interface:
open_ended_grading_interface = settings.OPEN_ENDED_GRADING_INTERFACE
open_ended_grading_interface['mock_peer_grading'] = settings.MOCK_PEER_GRADING
open_ended_grading_interface['mock_staff_grading'] = settings.MOCK_STAFF_GRADING
- if is_descriptor_combined_open_ended:
- s3_interface = {
- 'access_key' : getattr(settings,'AWS_ACCESS_KEY_ID',''),
- 'secret_access_key' : getattr(settings,'AWS_SECRET_ACCESS_KEY',''),
- 'storage_bucket_name' : getattr(settings,'AWS_STORAGE_BUCKET_NAME','openended')
- }
+ if needs_s3_interface:
+ s3_interface = {
+ 'access_key': getattr(settings, 'AWS_ACCESS_KEY_ID', ''),
+ 'secret_access_key': getattr(settings, 'AWS_SECRET_ACCESS_KEY', ''),
+ 'storage_bucket_name': getattr(settings, 'AWS_STORAGE_BUCKET_NAME', 'openended')
+ }
def inner_get_module(descriptor):
"""
diff --git a/lms/djangoapps/django_comment_client/base/views.py b/lms/djangoapps/django_comment_client/base/views.py
index e906fb5f7e..34e369c1ef 100644
--- a/lms/djangoapps/django_comment_client/base/views.py
+++ b/lms/djangoapps/django_comment_client/base/views.py
@@ -26,7 +26,7 @@ from course_groups.cohorts import get_cohort_id, is_commentable_cohorted
from django_comment_client.utils import JsonResponse, JsonError, extract, get_courseware_context
from django_comment_client.permissions import check_permissions_by_view, cached_has_permission
-from django_comment_client.models import Role
+from django_comment_common.models import Role
from courseware.access import has_access
log = logging.getLogger(__name__)
diff --git a/lms/djangoapps/django_comment_client/management/commands/assign_role.py b/lms/djangoapps/django_comment_client/management/commands/assign_role.py
index 1be3bff719..4e9321410c 100644
--- a/lms/djangoapps/django_comment_client/management/commands/assign_role.py
+++ b/lms/djangoapps/django_comment_client/management/commands/assign_role.py
@@ -1,7 +1,7 @@
from optparse import make_option
from django.core.management.base import BaseCommand, CommandError
-from django_comment_client.models import Role
+from django_comment_common.models import Role
from django.contrib.auth.models import User
diff --git a/lms/djangoapps/django_comment_client/management/commands/assign_roles_for_course.py b/lms/djangoapps/django_comment_client/management/commands/assign_roles_for_course.py
index 72100738d9..9ef4f3d0b1 100644
--- a/lms/djangoapps/django_comment_client/management/commands/assign_roles_for_course.py
+++ b/lms/djangoapps/django_comment_client/management/commands/assign_roles_for_course.py
@@ -7,7 +7,7 @@ Enrollments.
from django.core.management.base import BaseCommand, CommandError
from student.models import CourseEnrollment
-from django_comment_client.models import assign_default_role
+from django_comment_common.models import assign_default_role
class Command(BaseCommand):
diff --git a/lms/djangoapps/django_comment_client/management/commands/create_roles_for_existing.py b/lms/djangoapps/django_comment_client/management/commands/create_roles_for_existing.py
index d5ba0042fc..037bb292ec 100644
--- a/lms/djangoapps/django_comment_client/management/commands/create_roles_for_existing.py
+++ b/lms/djangoapps/django_comment_client/management/commands/create_roles_for_existing.py
@@ -7,7 +7,7 @@ Enrollments.
from django.core.management.base import BaseCommand, CommandError
from student.models import CourseEnrollment
-from django_comment_client.models import assign_default_role
+from django_comment_common.models import assign_default_role
class Command(BaseCommand):
diff --git a/lms/djangoapps/django_comment_client/management/commands/seed_permissions_roles.py b/lms/djangoapps/django_comment_client/management/commands/seed_permissions_roles.py
index 9d6eefd11d..1073d7dbcf 100644
--- a/lms/djangoapps/django_comment_client/management/commands/seed_permissions_roles.py
+++ b/lms/djangoapps/django_comment_client/management/commands/seed_permissions_roles.py
@@ -1,5 +1,5 @@
from django.core.management.base import BaseCommand, CommandError
-from django_comment_client.models import Role
+from django_comment_common.utils import seed_permissions_roles
class Command(BaseCommand):
@@ -13,26 +13,4 @@ class Command(BaseCommand):
raise CommandError("Too many arguments")
course_id = args[0]
- administrator_role = Role.objects.get_or_create(name="Administrator", course_id=course_id)[0]
- moderator_role = Role.objects.get_or_create(name="Moderator", course_id=course_id)[0]
- community_ta_role = Role.objects.get_or_create(name="Community TA", course_id=course_id)[0]
- student_role = Role.objects.get_or_create(name="Student", course_id=course_id)[0]
-
- for per in ["vote", "update_thread", "follow_thread", "unfollow_thread",
- "update_comment", "create_sub_comment", "unvote", "create_thread",
- "follow_commentable", "unfollow_commentable", "create_comment", ]:
- student_role.add_permission(per)
-
- for per in ["edit_content", "delete_thread", "openclose_thread",
- "endorse_comment", "delete_comment", "see_all_cohorts"]:
- moderator_role.add_permission(per)
-
- for per in ["manage_moderator"]:
- administrator_role.add_permission(per)
-
- moderator_role.inherit_permissions(student_role)
-
- # For now, Community TA == Moderator, except for the styling.
- community_ta_role.inherit_permissions(moderator_role)
-
- administrator_role.inherit_permissions(moderator_role)
+ seed_permissions_roles(course_id)
diff --git a/lms/djangoapps/django_comment_client/management/commands/show_permissions.py b/lms/djangoapps/django_comment_client/management/commands/show_permissions.py
index f24f183193..67fc29ea97 100644
--- a/lms/djangoapps/django_comment_client/management/commands/show_permissions.py
+++ b/lms/djangoapps/django_comment_client/management/commands/show_permissions.py
@@ -1,4 +1,5 @@
from django.core.management.base import BaseCommand, CommandError
+from django_comment_common.models import Permission, Role
from django.contrib.auth.models import User
diff --git a/lms/djangoapps/django_comment_client/models.py b/lms/djangoapps/django_comment_client/models.py
index 71e7a81f68..76d27be3bf 100644
--- a/lms/djangoapps/django_comment_client/models.py
+++ b/lms/djangoapps/django_comment_client/models.py
@@ -1,64 +1 @@
-import logging
-
-from django.db import models
-from django.contrib.auth.models import User
-
-from django.dispatch import receiver
-from django.db.models.signals import post_save
-
-from student.models import CourseEnrollment
-
-from courseware.courses import get_course_by_id
-
-FORUM_ROLE_ADMINISTRATOR = 'Administrator'
-FORUM_ROLE_MODERATOR = 'Moderator'
-FORUM_ROLE_COMMUNITY_TA = 'Community TA'
-FORUM_ROLE_STUDENT = 'Student'
-
-
-@receiver(post_save, sender=CourseEnrollment)
-def assign_default_role(sender, instance, **kwargs):
- if instance.user.is_staff:
- role = Role.objects.get_or_create(course_id=instance.course_id, name="Moderator")[0]
- else:
- role = Role.objects.get_or_create(course_id=instance.course_id, name="Student")[0]
-
- logging.info("assign_default_role: adding %s as %s" % (instance.user, role))
- instance.user.roles.add(role)
-
-
-class Role(models.Model):
- name = models.CharField(max_length=30, null=False, blank=False)
- users = models.ManyToManyField(User, related_name="roles")
- course_id = models.CharField(max_length=255, blank=True, db_index=True)
-
- def __unicode__(self):
- return self.name + " for " + (self.course_id if self.course_id else "all courses")
-
- def inherit_permissions(self, role): # TODO the name of this method is a little bit confusing,
- # since it's one-off and doesn't handle inheritance later
- if role.course_id and role.course_id != self.course_id:
- logging.warning("%s cannot inherit permissions from %s due to course_id inconsistency",
- self, role)
- for per in role.permissions.all():
- self.add_permission(per)
-
- def add_permission(self, permission):
- self.permissions.add(Permission.objects.get_or_create(name=permission)[0])
-
- def has_permission(self, permission):
- course = get_course_by_id(self.course_id)
- if self.name == FORUM_ROLE_STUDENT and \
- (permission.startswith('edit') or permission.startswith('update') or permission.startswith('create')) and \
- (not course.forum_posts_allowed):
- return False
-
- return self.permissions.filter(name=permission).exists()
-
-
-class Permission(models.Model):
- name = models.CharField(max_length=30, null=False, blank=False, primary_key=True)
- roles = models.ManyToManyField(Role, related_name="permissions")
-
- def __unicode__(self):
- return self.name
+# This file is intentionally blank. It has been moved to common/djangoapps/django_comment_common
diff --git a/lms/djangoapps/django_comment_client/permissions.py b/lms/djangoapps/django_comment_client/permissions.py
index cc3ead53e7..1a523a170a 100644
--- a/lms/djangoapps/django_comment_client/permissions.py
+++ b/lms/djangoapps/django_comment_client/permissions.py
@@ -1,4 +1,4 @@
-from .models import Role, Permission
+from django_comment_common.models import Role, Permission
from django.db.models.signals import post_save
from django.dispatch import receiver
from student.models import CourseEnrollment
diff --git a/lms/djangoapps/django_comment_client/tests.py b/lms/djangoapps/django_comment_client/tests.py
index a5cfce4dc7..8fd8ed7e2b 100644
--- a/lms/djangoapps/django_comment_client/tests.py
+++ b/lms/djangoapps/django_comment_client/tests.py
@@ -6,7 +6,7 @@ from django.test import TestCase
from student.models import CourseEnrollment
from django_comment_client.permissions import has_permission
-from django_comment_client.models import Role
+from django_comment_common.models import Role
class PermissionsTestCase(TestCase):
diff --git a/lms/djangoapps/django_comment_client/tests/factories.py b/lms/djangoapps/django_comment_client/tests/factories.py
index eb1d9477c3..4a82c8f1bb 100644
--- a/lms/djangoapps/django_comment_client/tests/factories.py
+++ b/lms/djangoapps/django_comment_client/tests/factories.py
@@ -1,5 +1,5 @@
from factory import DjangoModelFactory
-from django_comment_client.models import Role, Permission
+from django_comment_common.models import Role, Permission
class RoleFactory(DjangoModelFactory):
diff --git a/lms/djangoapps/django_comment_client/tests/test_models.py b/lms/djangoapps/django_comment_client/tests/test_models.py
index 0835c841e2..e45c883931 100644
--- a/lms/djangoapps/django_comment_client/tests/test_models.py
+++ b/lms/djangoapps/django_comment_client/tests/test_models.py
@@ -1,4 +1,4 @@
-import django_comment_client.models as models
+import django_comment_common.models as models
import django_comment_client.permissions as permissions
from django.test import TestCase
diff --git a/lms/djangoapps/django_comment_client/tests/test_utils.py b/lms/djangoapps/django_comment_client/tests/test_utils.py
index a7c0ce0a39..555264cb5f 100644
--- a/lms/djangoapps/django_comment_client/tests/test_utils.py
+++ b/lms/djangoapps/django_comment_client/tests/test_utils.py
@@ -1,6 +1,6 @@
from django.test import TestCase
from student.tests.factories import UserFactory, CourseEnrollmentFactory
-
+from django_comment_common.models import Role, Permission
from factories import RoleFactory
import django_comment_client.utils as utils
diff --git a/lms/djangoapps/django_comment_client/utils.py b/lms/djangoapps/django_comment_client/utils.py
index 0363607cfe..276956f0e9 100644
--- a/lms/djangoapps/django_comment_client/utils.py
+++ b/lms/djangoapps/django_comment_client/utils.py
@@ -14,7 +14,7 @@ from django.core.urlresolvers import reverse
from django.db import connection
from django.http import HttpResponse
from django.utils import simplejson
-from django_comment_client.models import Role
+from django_comment_common.models import Role
from django_comment_client.permissions import check_permissions_by_view
from xmodule.modulestore.exceptions import NoPathToItem
diff --git a/lms/djangoapps/instructor/tests/test_forum_admin.py b/lms/djangoapps/instructor/tests/test_forum_admin.py
index d2d58fb61c..7b4e729867 100644
--- a/lms/djangoapps/instructor/tests/test_forum_admin.py
+++ b/lms/djangoapps/instructor/tests/test_forum_admin.py
@@ -9,7 +9,7 @@ from django.test.utils import override_settings
from django.contrib.auth.models import Group
from django.core.urlresolvers import reverse
-from django_comment_client.models import Role, FORUM_ROLE_ADMINISTRATOR, \
+from django_comment_common.models import Role, FORUM_ROLE_ADMINISTRATOR, \
FORUM_ROLE_MODERATOR, FORUM_ROLE_COMMUNITY_TA, FORUM_ROLE_STUDENT
from django_comment_client.utils import has_forum_access
diff --git a/lms/djangoapps/instructor/views.py b/lms/djangoapps/instructor/views.py
index dd6748e691..00b1b918b3 100644
--- a/lms/djangoapps/instructor/views.py
+++ b/lms/djangoapps/instructor/views.py
@@ -27,7 +27,7 @@ from courseware.access import (has_access, get_access_group_name,
course_beta_test_group_name)
from courseware.courses import get_course_with_access
from courseware.models import StudentModule
-from django_comment_client.models import (Role,
+from django_comment_common.models import (Role,
FORUM_ROLE_ADMINISTRATOR,
FORUM_ROLE_MODERATOR,
FORUM_ROLE_COMMUNITY_TA)
diff --git a/lms/envs/common.py b/lms/envs/common.py
index 741d624ed7..e7bc9519d9 100644
--- a/lms/envs/common.py
+++ b/lms/envs/common.py
@@ -700,8 +700,7 @@ INSTALLED_APPS = (
# Discussion forums
'django_comment_client',
-
- # Student notes
+ 'django_comment_common',
'notes',
)
diff --git a/lms/envs/jasmine.py b/lms/envs/jasmine.py
index 2c30bc7de7..4a78ed8075 100644
--- a/lms/envs/jasmine.py
+++ b/lms/envs/jasmine.py
@@ -36,7 +36,12 @@ PIPELINE_JS['spec'] = {
}
JASMINE_TEST_DIRECTORY = PROJECT_ROOT + '/static/coffee'
+JASMINE_REPORT_DIR = os.environ.get('JASMINE_REPORT_DIR', 'reports/lms/jasmine')
+
+TEMPLATE_CONTEXT_PROCESSORS += ('settings_context_processor.context_processors.settings',)
+TEMPLATE_VISIBLE_SETTINGS = ('JASMINE_REPORT_DIR', )
STATICFILES_DIRS.append(REPO_ROOT/'node_modules/phantom-jasmine/lib')
+STATICFILES_DIRS.append(REPO_ROOT/'node_modules/jasmine-reporters/src')
-INSTALLED_APPS += ('django_jasmine', )
+INSTALLED_APPS += ('django_jasmine', 'settings_context_processor')
diff --git a/lms/static/sass/base/_base.scss b/lms/static/sass/base/_base.scss
index e62dd12541..6f43a02df7 100644
--- a/lms/static/sass/base/_base.scss
+++ b/lms/static/sass/base/_base.scss
@@ -2,8 +2,8 @@
// overflow-y: scroll;
// }
-body {
- background: rgb(250,250,250);
+html, body {
+ background: $body-bg;
font-family: $sans-serif;
font-size: 1em;
font-style: normal;
@@ -61,20 +61,20 @@ p + p, ul + p, ol + p {
p {
a:link, a:visited {
- color: $blue;
+ color: $link-color;
font: normal 1em/1em $serif;
text-decoration: none;
@include transition(all, 0.1s, linear);
&:hover {
- color: $blue;
+ color: $link-color;
text-decoration: underline;
}
}
}
a:link, a:visited {
- color: $blue;
+ color: $link-color;
font: normal 1em/1em $sans-serif;
text-decoration: none;
@include transition(all, 0.1s, linear);
@@ -87,8 +87,8 @@ a:link, a:visited {
.content-wrapper {
width: flex-grid(12);
margin: 0 auto;
+ background: $content-wrapper-bg;
padding-bottom: ($baseline*2);
- background: rgb(255,255,255);
}
.container {
@@ -164,7 +164,7 @@ mark {
display: none;
padding: 10px;
@include linear-gradient(top, rgba(0, 0, 0, .1), rgba(0, 0, 0, .0));
- background-color: $pink;
+ background-color: $site-status-color;
box-shadow: 0 -1px 0 rgba(0, 0, 0, .3) inset;
font-size: 14px;
diff --git a/lms/static/sass/base/_extends.scss b/lms/static/sass/base/_extends.scss
index 2998e25dca..d244eff55f 100644
--- a/lms/static/sass/base/_extends.scss
+++ b/lms/static/sass/base/_extends.scss
@@ -1,39 +1,30 @@
.faded-hr-divider {
- @include background-image(linear-gradient(180deg, rgba(200,200,200, 0) 0%,
- rgba(200,200,200, 1) 50%,
- rgba(200,200,200, 0)));
+ @include background-image($faded-hr-image-1);
height: 1px;
width: 100%;
}
.faded-hr-divider-medium {
- @include background-image(linear-gradient(180deg, rgba(240,240,240, 0) 0%,
- rgba(240,240,240, 1) 50%,
- rgba(240,240,240, 0)));
+ @include background-image($faded-hr-image-4);
height: 1px;
width: 100%;
}
.faded-hr-divider-light {
- @include background-image(linear-gradient(180deg, rgba(255,255,255, 0) 0%,
- rgba(255,255,255, 0.8) 50%,
- rgba(255,255,255, 0)));
+ @include background-image($faded-hr-image-5);
height: 1px;
width: 100%;
}
.faded-vertical-divider {
- @include background-image(linear-gradient(90deg, rgba(200,200,200, 0) 0%,
- rgba(200,200,200, 1) 50%,
- rgba(200,200,200, 0)));
+ @include background-image($faded-hr-image-1);
height: 100%;
width: 1px;
}
.faded-vertical-divider-light {
- @include background-image(linear-gradient(90deg, rgba(255,255,255, 0) 0%,
- rgba(255,255,255, 0.6) 50%,
- rgba(255,255,255, 0)));
+ @include background-image($faded-hr-image-6);
+ background: transparent;
height: 100%;
width: 1px;
}
@@ -66,14 +57,12 @@
}
.fade-right-hr-divider {
- @include background-image(linear-gradient(180deg, rgba(200,200,200, 0) 0%,
- rgba(200,200,200, 1)));
+ @include background-image($faded-hr-image-2);
border: none;
}
.fade-left-hr-divider {
- @include background-image(linear-gradient(180deg, rgba(200,200,200, 1) 0%,
- rgba(200,200,200, 0)));
+ @include background-image($faded-hr-image-3);
border: none;
}
diff --git a/lms/static/sass/base/_variables.scss b/lms/static/sass/base/_variables.scss
index ddbd930323..6bd593c28c 100644
--- a/lms/static/sass/base/_variables.scss
+++ b/lms/static/sass/base/_variables.scss
@@ -14,6 +14,14 @@ $monospace: Monaco, 'Bitstream Vera Sans Mono', 'Lucida Console', monospace;
$body-font-family: $sans-serif;
$serif: $georgia;
+$body-font-size: em(14);
+$body-line-height: golden-ratio(.875em, 1);
+$base-font-color: rgb(60,60,60);
+$baseFontColor: rgb(60,60,60);
+$base-font-color: rgb(60,60,60);
+$lighter-base-font-color: rgb(100,100,100);
+$very-light-text: #fff;
+
$white: rgb(255,255,255);
$black: rgb(0,0,0);
$blue: rgb(29,157,217);
@@ -52,6 +60,66 @@ $baseFontColor: rgb(60,60,60);
$lighter-base-font-color: rgb(100,100,100);
$text-color: $dark-gray;
-$body-font-family: $sans-serif;
-$body-font-size: em(14);
-$body-line-height: golden-ratio(.875em, 1);
+$body-bg: rgb(250,250,250);
+$header-image: linear-gradient(-90deg, rgba(255,255,255, 1), rgba(230,230,230, 0.9));
+$header-bg: transparent;
+$courseware-header-image: linear-gradient(top, #fff, #eee);
+$courseware-header-bg: transparent;
+$footer-bg: transparent;
+$courseware-footer-border: none;
+$courseware-footer-shadow: none;
+$courseware-footer-margin: 0px;
+
+$button-bg-image: linear-gradient(#fff 0%, rgb(250,250,250) 50%, rgb(237,237,237) 50%, rgb(220,220,220) 100%);
+$button-bg-color: transparent;
+$button-bg-hover-color: #fff;
+
+$faded-hr-image-1: linear-gradient(180deg, rgba(200,200,200, 0) 0%, rgba(200,200,200, 1) 50%, rgba(200,200,200, 0));
+$faded-hr-image-2: linear-gradient(180deg, rgba(200,200,200, 0) 0%, rgba(200,200,200, 1));
+$faded-hr-image-3: linear-gradient(180deg, rgba(200,200,200, 1) 0%, rgba(200,200,200, 0));
+$faded-hr-image-4: linear-gradient(180deg, rgba(240,240,240, 0) 0%, rgba(240,240,240, 1) 50%, rgba(240,240,240, 0));
+$faded-hr-image-5: linear-gradient(180deg, rgba(255,255,255, 0) 0%, rgba(255,255,255, 0.8) 50%, rgba(255,255,255, 0));
+$faded-hr-image-6: linear-gradient(90deg, rgba(255,255,255, 0) 0%, rgba(255,255,255, 0.6) 50%, rgba(255,255,255, 0));
+
+$dashboard-profile-header-image: linear-gradient(-90deg, rgb(255,255,255), rgb(245,245,245));
+$dashboard-profile-header-color: transparent;
+$dashboard-profile-color: rgb(252,252,252);
+$dot-color: $light-gray;
+
+$content-wrapper-bg: rgb(255,255,255);
+$course-bg-color: #d6d6d6;
+$course-bg-image: url(../images/bg-texture.png);
+
+$course-profile-bg: rgb(245,245,245);
+$course-header-bg: rgba(255,255,255, 0.93);
+
+$border-color-1: rgb(190,190,190);
+$border-color-2: rgb(200,200,200);
+$border-color-3: rgb(100,100,100);
+$border-color-4: rgb(252,252,252);
+
+$link-color: $blue;
+$link-hover: $pink;
+$selection-color-1: $pink;
+$selection-color-2: #444;
+$site-status-color: $pink;
+
+$button-color: $blue;
+$button-archive-color: #eee;
+
+$shadow-color: $blue;
+
+$sidebar-chapter-bg-top: rgba(255, 255, 255, .6);
+$sidebar-chapter-bg-bottom: rgba(255, 255, 255, 0);
+$sidebar-chapter-bg: #eee;
+$sidebar-active-image: linear-gradient(top, #e6e6e6, #d6d6d6);
+
+$form-bg-color: #fff;
+$modal-bg-color: rgb(245,245,245);
+
+//-----------------
+// CSS BG Images
+//-----------------
+$homepage-bg-image: '../images/homepage-bg.jpg';
+
+$video-thumb-url: '../images/courses/video-thumb.jpg';
\ No newline at end of file
diff --git a/lms/static/sass/course/_info.scss b/lms/static/sass/course/_info.scss
index bfd90505cf..741a7f9a22 100644
--- a/lms/static/sass/course/_info.scss
+++ b/lms/static/sass/course/_info.scss
@@ -117,7 +117,7 @@ div.info-wrapper {
@include transition(all .2s);
h4 {
- color: $blue;
+ color: $link-color;
font-size: 1em;
font-weight: normal;
padding-left: 30px;
diff --git a/lms/static/sass/course/base/_base.scss b/lms/static/sass/course/base/_base.scss
index 6183c8a675..584412ca22 100644
--- a/lms/static/sass/course/base/_base.scss
+++ b/lms/static/sass/course/base/_base.scss
@@ -1,7 +1,8 @@
body {
min-width: 980px;
min-height: 100%;
- background: url(../images/bg-texture.png) #d6d6d6;
+ background-image: $course-bg-image;
+ background-color: $course-bg-color;
}
body, h1, h2, h3, h4, h5, h6, p, p a:link, p a:visited, a, label {
@@ -34,7 +35,7 @@ a {
width: 100%;
border-radius: 3px;
border: 1px solid $outer-border-color;
- background: #fff;
+ background: $body-bg;
@include box-shadow(0 1px 2px rgba(0, 0, 0, 0.05));
}
}
@@ -49,8 +50,8 @@ textarea,
input[type="text"],
input[type="email"],
input[type="password"] {
- background: rgb(250,250,250);
- border: 1px solid rgb(200,200,200);
+ background: $body-bg;
+ border: 1px solid $border-color-2;
@include border-radius(0);
@include box-shadow(0 1px 0 0 rgba(255,255,255, 0.6), inset 0 0 3px 0 rgba(0,0,0, 0.1));
@include box-sizing(border-box);
@@ -65,7 +66,7 @@ input[type="password"] {
}
&:focus {
- border-color: lighten($blue, 20%);
+ border-color: lighten($link-color, 20%);
@include box-shadow(0 0 6px 0 rgba($blue, 0.4), inset 0 0 4px 0 rgba(0,0,0, 0.15));
outline: none;
}
@@ -94,7 +95,7 @@ img {
}
::selection, ::-moz-selection, ::-webkit-selection {
- background: #444;
+ background: $selection-color-2;
color: #fff;
}
@@ -143,7 +144,7 @@ img {
max-width: 350px;
padding: 15px 20px 17px;
border-radius: 3px;
- border: 1px solid #333;
+ border: 1px solid $border-color-3;
background: -webkit-linear-gradient(top, rgba(255, 255, 255, .1), rgba(255, 255, 255, 0)) rgba(30, 30, 30, .92);
box-shadow: 0 1px 3px rgba(0, 0, 0, .3), 0 1px 0 rgba(255, 255, 255, .1) inset;
font-size: 13px;
diff --git a/lms/static/sass/course/base/_extends.scss b/lms/static/sass/course/base/_extends.scss
index bcb93a3645..a94a9511fe 100644
--- a/lms/static/sass/course/base/_extends.scss
+++ b/lms/static/sass/course/base/_extends.scss
@@ -1,5 +1,5 @@
h1.top-header {
- border-bottom: 1px solid #e3e3e3;
+ border-bottom: 1px solid $border-color-2;
text-align: left;
font-size: em(24);
font-weight: 100;
diff --git a/lms/static/sass/course/courseware/_sidebar.scss b/lms/static/sass/course/courseware/_sidebar.scss
index 81b497d4f9..6cf6f6a602 100644
--- a/lms/static/sass/course/courseware/_sidebar.scss
+++ b/lms/static/sass/course/courseware/_sidebar.scss
@@ -2,7 +2,7 @@ section.course-index {
@extend .sidebar;
@extend .tran;
@include border-radius(3px 0 0 3px);
- border-right: 1px solid #ddd;
+ border-right: 1px solid $border-color-2;
#open_close_accordion {
display: none;
@@ -70,8 +70,8 @@ section.course-index {
width: 100% !important;
@include box-sizing(border-box);
padding: 11px 14px;
- @include linear-gradient(top, rgba(255, 255, 255, .6), rgba(255, 255, 255, 0));
- background-color: #eee;
+ @include linear-gradient(top, $sidebar-chapter-bg-top, $sidebar-chapter-bg-bottom);
+ background-color: $sidebar-chapter-bg;
@include box-shadow(0 1px 0 #fff inset, 0 -1px 0 rgba(0, 0, 0, .1) inset);
@include transition(background-color .1s);
@@ -169,9 +169,9 @@ section.course-index {
}
> a {
- border: 1px solid #bbb;
+ border: 1px solid $border-color-1;
@include box-shadow(0 1px 0 rgba(255, 255, 255, .35) inset);
- @include linear-gradient(top, #e6e6e6, #d6d6d6);
+ background: $sidebar-active-image;
&:after {
opacity: 1;
diff --git a/lms/static/sass/course/layout/_courseware_header.scss b/lms/static/sass/course/layout/_courseware_header.scss
index e27a6e99d8..4d8f000668 100644
--- a/lms/static/sass/course/layout/_courseware_header.scss
+++ b/lms/static/sass/course/layout/_courseware_header.scss
@@ -75,9 +75,9 @@ header.global.slim {
login {
display: block;
- @include background-image(linear-gradient(-90deg, lighten($blue, 8%), lighten($blue, 5%) 50%, $blue 50%, darken($blue, 10%) 100%));
+ @include background-image(linear-gradient(-90deg, lighten($link-color, 8%), lighten($link-color, 5%) 50%, $link-color 50%, darken($link-color, 10%) 100%));
border: 1px solid transparent;
- border-color: darken($blue, 10%);
+ border-color: darken($link-color, 10%);
@include border-radius(3px);
@include box-sizing(border-box);
@include box-shadow(0 1px 0 0 rgba(255,255,255, 0.6));
@@ -97,7 +97,7 @@ header.global.slim {
vertical-align: middle;
&:hover, &.active {
- @include background-image(linear-gradient(-90deg, $blue, $blue 50%, $blue 50%, $blue 100%));
+ @include background-image(linear-gradient(-90deg, $link-color, $link-color 50%, $link-color 50%, $link-color 100%));
}
}
}
diff --git a/lms/static/sass/course/layout/_footer.scss b/lms/static/sass/course/layout/_footer.scss
index 7abf35a819..699846e781 100644
--- a/lms/static/sass/course/layout/_footer.scss
+++ b/lms/static/sass/course/layout/_footer.scss
@@ -1,4 +1,5 @@
footer {
- border: none;
- box-shadow: none;
+ border: $courseware-footer-border;
+ box-shadow: $courseware-footer-shadow;
+ margin-top: $courseware-footer-margin;
}
\ No newline at end of file
diff --git a/lms/static/sass/course/wiki/_wiki.scss b/lms/static/sass/course/wiki/_wiki.scss
index 1bc38abd9a..d064b6d345 100644
--- a/lms/static/sass/course/wiki/_wiki.scss
+++ b/lms/static/sass/course/wiki/_wiki.scss
@@ -113,7 +113,7 @@ section.wiki {
}
&:focus {
- border-color: $blue;
+ border-color: $link-color;
}
}
}
@@ -276,7 +276,7 @@ section.wiki {
li {
&.active {
a {
- color: $blue;
+ color: $link-color;
.icon-view,
.icon-home {
diff --git a/lms/static/sass/multicourse/_course_about.scss b/lms/static/sass/multicourse/_course_about.scss
index 195760721e..9eab7c0a4f 100644
--- a/lms/static/sass/multicourse/_course_about.scss
+++ b/lms/static/sass/multicourse/_course_about.scss
@@ -4,11 +4,11 @@
}
header.course-profile {
- background: rgb(245,245,245);
- @include background-image(url('/static/images/homepage-bg.jpg'));
+ background: $course-profile-bg;
+ @include background-image(url($homepage-bg-image));
background-size: cover;
@include box-shadow(0 1px 80px 0 rgba(0,0,0, 0.5));
- border-bottom: 1px solid rgb(100,100,100);
+ border-bottom: 1px solid $border-color-3;
@include box-shadow(inset 0 1px 5px 0 rgba(0,0,0, 0.1));
height: 280px;
margin-top: -69px;
@@ -18,8 +18,8 @@
width: 100%;
.intro-inner-wrapper {
- background: rgba(255,255,255, 0.93);
- border: 1px solid rgb(100,100,100);
+ background: $course-header-bg;
+ border: 1px solid $border-color-3;
@include box-shadow(0 4px 25px 0 rgba(0,0,0, 0.5));
@include box-sizing(border-box);
@include clearfix;
@@ -44,7 +44,7 @@
z-index: 2;
> hgroup {
- border-bottom: 1px solid rgb(210,210,210);
+ border-bottom: 1px solid $border-color-2;
@include box-shadow(0 1px 0 0 rgba(255,255,255, 0.6));
margin-bottom: 20px;
padding-bottom: 20px;
@@ -68,7 +68,7 @@
text-transform: none;
&:hover {
- color: $blue;
+ color: $link-color;
}
}
}
@@ -85,7 +85,7 @@
text-transform: none;
&:hover {
- color: $blue;
+ color: $link-color;
}
}
}
@@ -99,7 +99,7 @@
width: flex-grid(12);
> a.find-courses, a.register {
- @include button(shiny, $blue);
+ @include button(shiny, $button-color);
@include box-sizing(border-box);
@include border-radius(3px);
display: block;
@@ -122,7 +122,7 @@
}
strong {
- @include button(shiny, $blue);
+ @include button(shiny, $button-color);
@include box-sizing(border-box);
@include border-radius(3px);
display: block;
@@ -140,10 +140,10 @@
}
span.register {
- background: lighten($blue, 20%);
- border: 1px solid $blue;
+ background: $button-archive-color;
+ border: 1px solid darken($button-archive-color, 50%);
@include box-sizing(border-box);
- color: darken($blue, 20%);
+ color: darken($button-archive-color, 50%);
display: block;
letter-spacing: 1px;
padding: 10px 0px 8px;
@@ -176,7 +176,7 @@
z-index: 2;
.hero {
- border: 1px solid rgb(100,100,100);
+ border: 1px solid $border-color-3;
height: 100%;
overflow: hidden;
position: relative;
@@ -235,7 +235,7 @@
@include clearfix;
nav {
- border-bottom: 1px solid rgb(220,220,220);
+ border-bottom: 1px solid $border-color-2;
@include box-sizing(border-box);
@include clearfix;
margin: 40px 0;
@@ -262,7 +262,7 @@
}
&:hover, &.active {
- border-color: rgb(200,200,200);
+ border-color: $border-color-2;
color: $base-font-color;
text-decoration: none;
}
@@ -296,7 +296,7 @@
.teacher-image {
background: rgb(255,255,255);
- border: 1px solid rgb(200,200,200);
+ border: 1px solid $border-color-2;
height: 115px;
float: left;
margin: 0 15px 0px 0;
@@ -351,7 +351,7 @@
> section {
@include box-shadow(inset 0 0 3px 0 rgba(0,0,0, 0.15));
- border: 1px solid rgb(200,200,200);
+ border: 1px solid $border-color-2;
&.course-summary {
padding: 16px 20px 30px;
@@ -401,7 +401,7 @@
}
a.university-name {
- border-right: 1px solid rgb(200,200,200);
+ border-right: 1px solid $border-color-2;
color: $base-font-color;
font-family: $sans-serif;
font-style: italic;
@@ -498,12 +498,12 @@
li {
@include clearfix;
- border-bottom: 1px dotted rgb(220,220,220);
+ border-bottom: 1px dotted $border-color-2;
margin-bottom: 20px;
padding-bottom: 10px;
&.prerequisites {
- border: 1px solid rgb(220,220,220);
+ border: 1px solid $border-color-2;
margin: 0 -10px 0;
padding: 10px;
diff --git a/lms/static/sass/multicourse/_courses.scss b/lms/static/sass/multicourse/_courses.scss
index 45ecfcd23f..ac31da4d2a 100644
--- a/lms/static/sass/multicourse/_courses.scss
+++ b/lms/static/sass/multicourse/_courses.scss
@@ -1,12 +1,13 @@
.find-courses, .university-profile {
- background: rgb(252,252,252);
+ background: $course-profile-bg;
padding-bottom: 60px;
header.search {
- background: rgb(240,240,240);
+ background: $course-profile-bg;
background-size: cover;
+ @include background-image(url($homepage-bg-image));
background-position: center top !important;
- border-bottom: 1px solid rgb(100,100,100);
+ border-bottom: 1px solid $border-color-3;
@include box-shadow(inset 0 -1px 8px 0 rgba(0,0,0, 0.2), inset 0 1px 12px 0 rgba(0,0,0, 0.3));
height: 430px;
margin-top: -69px;
@@ -24,8 +25,8 @@
> hgroup {
background: #FFF;
- background: rgba(255,255,255, 0.93);
- border: 1px solid rgb(100,100,100);
+ background: $course-header-bg;
+ border: 1px solid $border-color-3;
@include box-shadow(0 4px 25px 0 rgba(0,0,0, 0.5));
padding: 20px 30px;
position: relative;
@@ -83,7 +84,7 @@
}
section.message {
- border-top: 1px solid rgb(220,220,220);
+ border-top: 1px solid $border-color-2;
@include clearfix;
margin-top: 20px;
padding-top: 60px;
diff --git a/lms/static/sass/multicourse/_dashboard.scss b/lms/static/sass/multicourse/_dashboard.scss
index cc54b9b242..b173647550 100644
--- a/lms/static/sass/multicourse/_dashboard.scss
+++ b/lms/static/sass/multicourse/_dashboard.scss
@@ -30,8 +30,9 @@
width: flex-grid(3);
header.profile {
- @include background-image(linear-gradient(-90deg, rgb(255,255,255), rgb(245,245,245)));
- border: 1px solid rgb(200,200,200);
+ @include background-image($dashboard-profile-header-image);
+ background-color: $dashboard-profile-header-color;
+ border: 1px solid $border-color-2;
@include border-radius(4px);
@include box-sizing(border-box);
width: flex-grid(12);
@@ -53,8 +54,8 @@
padding: 0px 10px;
> ul {
- background: rgb(252,252,252);
- border: 1px solid rgb(200,200,200);
+ background: $dashboard-profile-color;
+ border: 1px solid $border-color-2;
border-top: none;
//@include border-bottom-radius(4px);
@include box-sizing(border-box);
@@ -66,7 +67,7 @@
li {
@include clearfix;
- border-bottom: 1px dotted rgb(220,220,220);
+ border-bottom: 1px dotted $border-color-2;
list-style: none;
margin-bottom: 15px;
padding-bottom: 17px;
@@ -128,8 +129,8 @@
.news-carousel {
@include clearfix;
margin: 30px 10px 0;
- border: 1px solid rgb(200,200,200);
- background: rgb(252,252,252);
+ border: 1px solid $border-color-2;
+ background: $dashboard-profile-color;
@include box-shadow(inset 0 0 3px 0 rgba(0,0,0, 0.15));
* {
@@ -156,14 +157,14 @@
width: 11px;
height: 11px;
border-radius: 11px;
- background: $light-gray;
+ background: $dot-color;
&:hover {
- background: #ccc;
+ background: $lighter-base-font-color;
}
&.current {
- background: $blue;
+ background: $link-color;
}
}
@@ -201,7 +202,7 @@
img {
width: 100%;
- border: 1px solid $light-gray;
+ border: 1px solid $border-color-1;
}
}
@@ -229,7 +230,7 @@
width: flex-grid(9);
> header {
- border-bottom: 1px solid rgb(210,210,210);
+ border-bottom: 1px solid $border-color-2;
margin-bottom: 30px;
}
@@ -246,8 +247,9 @@
a {
background: rgb(240,240,240);
- @include background-image(linear-gradient(-90deg, rgb(245,245,245) 0%, rgb(243,243,243) 50%, rgb(237,237,237) 50%, rgb(235,235,235) 100%));
- border: 1px solid rgb(220,220,220);
+ @include background-image($button-bg-image);
+ background-color: $button-bg-color;
+ border: 1px solid $border-color-2;
@include border-radius(4px);
@include box-shadow(0 1px 8px 0 rgba(0,0,0, 0.1));
@include box-sizing(border-box);
@@ -260,7 +262,7 @@
text-shadow: 0 1px rgba(255,255,255, 0.6);
&:hover {
- color: $blue;
+ color: $link-color;
text-decoration: none;
}
}
@@ -272,7 +274,7 @@
margin-right: flex-gutter();
margin-bottom: 50px;
padding-bottom: 50px;
- border-bottom: 1px solid $light-gray;
+ border-bottom: 1px solid $border-color-1;
position: relative;
width: flex-grid(12);
z-index: 20;
@@ -343,7 +345,7 @@
.course-status {
background: $yellow;
- border: 1px solid rgb(200,200,200);
+ border: 1px solid $border-color-2;
@include box-shadow(0 1px 0 0 rgba(255,255,255, 0.6));
margin-top: 17px;
margin-right: flex-gutter();
@@ -362,7 +364,7 @@
.course-status-completed {
background: #ccc;
- color: #fff;
+ color: $very-light-text;
p {
color: #222;
@@ -374,7 +376,7 @@
}
.enter-course {
- @include button(simple, $blue);
+ @include button(simple, $button-color);
@include box-sizing(border-box);
@include border-radius(3px);
display: block;
@@ -386,7 +388,7 @@
margin-top: 16px;
&.archived {
- @include button(simple, #eee);
+ @include button(simple, $button-archive-color);
font: normal 15px/1.6rem $sans-serif;
padding: 6px 32px 7px;
diff --git a/lms/static/sass/multicourse/_home.scss b/lms/static/sass/multicourse/_home.scss
index b5546aa470..ea8ddaf654 100644
--- a/lms/static/sass/multicourse/_home.scss
+++ b/lms/static/sass/multicourse/_home.scss
@@ -7,15 +7,15 @@
}
> header {
- background: rgb(255,255,255);
- @include background-image(url('/static/images/homepage-bg.jpg'));
+ background: $dashboard-profile-color;
+ @include background-image(url($homepage-bg-image));
background-size: cover;
- border-bottom: 1px solid rgb(80,80,80);
- @include box-shadow(0 1px 0 0 rgba(255,255,255, 0.9), inset 0 -1px 5px 0 rgba(0,0,0, 0.1));
+ border-bottom: 1px solid $border-color-3;
+ @include box-shadow(0 1px 0 0 $course-header-bg, inset 0 -1px 5px 0 rgba(0,0,0, 0.1));
@include clearfix;
height: 460px;
- margin-top: -69px;
overflow: hidden;
+ margin-top: -69px;
padding: 0px;
width: flex-grid(12);
@@ -31,8 +31,8 @@
.title {
background: #FFF;
- background: rgba(255,255,255, 0.93);
- border: 1px solid rgb(100,100,100);
+ background: $course-header-bg;
+ border: 1px solid $border-color-3;
@include box-shadow(0 4px 25px 0 rgba(0,0,0, 0.5));
@include box-sizing(border-box);
min-height: 120px;
@@ -80,8 +80,8 @@
.media {
background: #FFF;
- background: rgba(255,255,255, 0.93);
- border: 1px solid rgb(100,100,100);
+ background: $course-header-bg;
+ border: 1px solid $border-color-3;
border-left: 0;
@include box-sizing(border-box);
// @include box-shadow(0 4px 25px 0 rgba(0,0,0, 0.5));
@@ -101,7 +101,7 @@
height: 100%;
overflow: hidden;
position: relative;
- background: url('../images/courses/video-thumb.jpg') center no-repeat;
+ background: url($video-thumb-url) center no-repeat;
@include background-size(cover);
.play-intro {
@@ -164,9 +164,9 @@
> h2 {
@include background-image(linear-gradient(-90deg, rgb(250,250,250), rgb(230,230,230)));
- border: 1px solid rgb(200,200,200);
+ border: 1px solid $border-color-2;
@include border-radius(4px);
- border-top-color: rgb(190,190,190);
+ border-top-color: $border-color-1;
@include box-shadow(inset 0 0 0 1px rgba(255,255,255, 0.4), 0 0px 12px 0 rgba(0,0,0, 0.2));
color: $lighter-base-font-color;
letter-spacing: 1px;
@@ -180,7 +180,7 @@
}
.university-partners {
- border-bottom: 1px solid rgb(210,210,210);
+ border-bottom: 1px solid $border-color-2;
margin-bottom: 0px;
overflow: hidden;
position: relative;
@@ -366,13 +366,13 @@
}
.more-info {
- border: 1px solid rgb(200,200,200);
+ border: 1px solid $border-color-2;
margin-bottom: 80px;
width: flex-grid(12);
header {
@include background-image(linear-gradient(-90deg, rgb(250,250,250), rgb(230,230,230)));
- border-bottom: 1px solid rgb(200,200,200);
+ border-bottom: 1px solid $border-color-2;
@include clearfix;
padding: 10px 20px 8px;
position: relative;
@@ -415,14 +415,14 @@
width: flex-grid(12);
.blog-posts {
- border-bottom: 1px solid rgb(220,220,220);
+ border-bottom: 1px solid $border-color-2;
margin-bottom: 20px;
padding-bottom: 20px;
@include clearfix;
> article {
border: 1px dotted transparent;
- border-color: rgb(220,220,220);
+ border-color: $border-color-2;
@include box-sizing(border-box);
@include clearfix;
float: left;
@@ -432,8 +432,8 @@
width: flex-grid(4);
&:hover {
- background: rgb(248,248,248);
- border: 1px solid rgb(220,220,220);
+ background: $body-bg;
+ border: 1px solid $border-color-2;
@include box-shadow(inset 0 0 3px 0 rgba(0,0,0, 0.1));
}
@@ -442,7 +442,7 @@
}
.post-graphics {
- border: 1px solid rgb(190,190,190);
+ border: 1px solid $border-color-1;
@include box-sizing(border-box);
display: block;
float: left;
diff --git a/lms/static/sass/shared/_course_object.scss b/lms/static/sass/shared/_course_object.scss
index e99559a49f..f78c483925 100644
--- a/lms/static/sass/shared/_course_object.scss
+++ b/lms/static/sass/shared/_course_object.scss
@@ -31,8 +31,8 @@
}
.course {
- background: rgb(250,250,250);
- border: 1px solid rgb(180,180,180);
+ background: $body-bg;
+ border: 1px solid $border-color-1;
@include border-radius(2px);
@include box-sizing(border-box);
@include box-shadow(0 1px 10px 0 rgba(0,0,0, 0.15), inset 0 0 0 1px rgba(255,255,255, 0.9));
@@ -42,7 +42,7 @@
@include transition(all, 0.15s, linear);
.status {
- background: $blue;
+ background: $link-color;
color: white;
font-size: 10px;
left: 10px;
@@ -55,7 +55,7 @@
}
.status:after {
- border-bottom: 6px solid shade($blue, 50%);
+ border-bottom: 6px solid shade($link-color, 50%);
border-right: 6px solid transparent;
content: "";
display: block;
@@ -90,7 +90,7 @@
}
.inner-wrapper {
- border: 1px solid rgba(255,255,255, 1);
+ border: 1px solid $border-color-4;
height: 100%;
height: 200px;
overflow: hidden;
@@ -116,12 +116,12 @@
text-decoration: none;
.info-link {
- color: $blue;
+ color: $link-color;
opacity: 1;
}
h2 {
- color: $blue;
+ color: $link-color;
}
}
@@ -176,7 +176,7 @@
// }
.info {
- background: rgb(255,255,255);
+ background: $content-wrapper-bg;
height: 220px + 130px;
left: 0px;
position: absolute;
@@ -221,14 +221,14 @@
width: 100%;
.university {
- border-right: 1px solid rgb(200,200,200);
+ border-right: 1px solid $border-color-2;
color: $lighter-base-font-color;
letter-spacing: 1px;
margin-right: 10px;
padding-right: 10px;
&:hover {
- color: $blue;
+ color: $link-color;
}
}
@@ -240,9 +240,9 @@
}
&:hover {
- background: rgb(245,245,245);
- border-color: rgb(170,170,170);
- @include box-shadow(0 1px 16px 0 rgba($blue, 0.4));
+ background: $course-profile-bg;
+ border-color: $border-color-1;
+ @include box-shadow(0 1px 16px 0 rgba($shadow-color, 0.4));
.info {
top: -150px;
diff --git a/lms/static/sass/shared/_footer.scss b/lms/static/sass/shared/_footer.scss
index d891ff408b..e3e99ae301 100644
--- a/lms/static/sass/shared/_footer.scss
+++ b/lms/static/sass/shared/_footer.scss
@@ -159,4 +159,4 @@
width: 360px;
}
}
-}
\ No newline at end of file
+}
diff --git a/lms/static/sass/shared/_forms.scss b/lms/static/sass/shared/_forms.scss
index 79d476f420..3350081850 100644
--- a/lms/static/sass/shared/_forms.scss
+++ b/lms/static/sass/shared/_forms.scss
@@ -15,8 +15,8 @@ input[type="text"],
input[type="email"],
input[type="password"],
input[type="tel"] {
- background: rgb(250,250,250);
- border: 1px solid rgb(200,200,200);
+ background: $form-bg-color;
+ border: 1px solid $border-color-2;
@include border-radius(3px);
@include box-shadow(0 1px 0 0 rgba(255,255,255, 0.6), inset 0 0 3px 0 rgba(0,0,0, 0.1));
@include box-sizing(border-box);
@@ -31,8 +31,8 @@ input[type="tel"] {
}
&:focus {
- border-color: lighten($blue, 20%);
- @include box-shadow(0 0 6px 0 rgba($blue, 0.4), inset 0 0 4px 0 rgba(0,0,0, 0.15));
+ border-color: darken($button-archive-color, 50%);
+ @include box-shadow(0 0 6px 0 darken($button-archive-color, 50%), inset 0 0 4px 0 rgba(0,0,0, 0.15));
outline: none;
}
}
@@ -46,7 +46,7 @@ input[type="button"],
button,
.button {
@include border-radius(3px);
- @include button(shiny, $blue);
+ @include button(shiny, $button-color);
font: normal 1.2rem/1.6rem $sans-serif;
letter-spacing: 1px;
padding: 4px 20px;
diff --git a/lms/static/sass/shared/_header.scss b/lms/static/sass/shared/_header.scss
index 5eb453448c..6987b35c84 100644
--- a/lms/static/sass/shared/_header.scss
+++ b/lms/static/sass/shared/_header.scss
@@ -54,8 +54,7 @@ header.global {
li.secondary {
> a {
- color: $lighter-base-font-color;
- color: $blue;
+ color: $link-color;
display: block;
font-family: $sans-serif;
@include inline-block;
@@ -78,9 +77,9 @@ header.global {
margin-right: 5px;
> a {
- @include background-image(linear-gradient(#fff 0%, rgb(250,250,250) 50%, rgb(237,237,237) 50%, rgb(220,220,220) 100%));
- border: 1px solid transparent;
- border-color: rgb(200,200,200);
+ @include background-image($button-bg-image);
+ background-color: $button-bg-color;
+ border: 1px solid $border-color-2;
@include border-radius(3px);
@include box-sizing(border-box);
@include box-shadow(0 1px 0 0 rgba(255,255,255, 0.6));
@@ -101,7 +100,7 @@ header.global {
}
&:hover, &.active {
- background: #FFF;
+ background: $button-bg-hover-color;
}
}
}
@@ -159,10 +158,10 @@ header.global {
}
ul.dropdown-menu {
- background: rgb(252,252,252);
+ background: $border-color-4;
@include border-radius(4px);
@include box-shadow(0 2px 24px 0 rgba(0,0,0, 0.3));
- border: 1px solid rgb(100,100,100);
+ border: 1px solid $border-color-3;
display: none;
padding: 5px 10px;
position: absolute;
@@ -178,12 +177,12 @@ header.global {
&::before {
background: transparent;
border: {
- top: 6px solid rgba(252,252,252, 1);
- right: 6px solid rgba(252,252,252, 1);
+ top: 6px solid $border-color-4;
+ right: 6px solid $border-color-4;
bottom: 6px solid transparent;
left: 6px solid transparent;
}
- @include box-shadow(1px 0 0 0 rgb(0,0,0), 0 -1px 0 0 rgb(0,0,0));
+ @include box-shadow(1px 0 0 0 $border-color-3, 0 -1px 0 0 $border-color-3);
content: "";
display: block;
height: 0px;
@@ -196,7 +195,7 @@ header.global {
li {
display: block;
- border-top: 1px dotted rgba(200,200,200, 1);
+ border-top: 1px dotted $border-color-2;
@include box-shadow(inset 0 1px 0 0 rgba(255,255,255, 0.05));
&:first-child {
@@ -208,7 +207,7 @@ header.global {
border: 1px solid transparent;
@include border-radius(3px);
@include box-sizing(border-box);
- color: $blue;
+ color: $link-color;
cursor: pointer;
display: block;
margin: 5px 0px;
@@ -328,4 +327,4 @@ header.global {
text-decoration: none;
color: $m-blue-s1 !important;
}
-}
\ No newline at end of file
+}
diff --git a/lms/static/sass/shared/_modal.scss b/lms/static/sass/shared/_modal.scss
index 8ff58c1c14..7a51213dee 100644
--- a/lms/static/sass/shared/_modal.scss
+++ b/lms/static/sass/shared/_modal.scss
@@ -52,7 +52,7 @@
}
.inner-wrapper {
- background: rgb(245,245,245);
+ background: $modal-bg-color;
@include border-radius(0px);
border: 1px solid rgba(0, 0, 0, 0.9);
@include box-shadow(inset 0 1px 0 0 rgba(255, 255, 255, 0.7));
@@ -149,7 +149,7 @@
}
label {
- color: #646464;
+ color: $text-color;
&.field-error {
display: block;
diff --git a/lms/templates/email_change_failed.html b/lms/templates/email_change_failed.html
new file mode 100644
index 0000000000..e228df4a9c
--- /dev/null
+++ b/lms/templates/email_change_failed.html
@@ -0,0 +1,3 @@
+
E-mail change failed.
+
+
We were unable to send a confirmation email to ${email}
diff --git a/package.json b/package.json
index 7fa287018a..2dd67d5be4 100644
--- a/package.json
+++ b/package.json
@@ -3,6 +3,7 @@
"version": "0.1.0",
"dependencies": {
"coffee-script": "1.6.X",
- "phantom-jasmine": "0.1.0"
+ "phantom-jasmine": "0.1.0",
+ "jasmine-reporters": "0.2.1"
}
}
diff --git a/pylintrc b/pylintrc
index 792079ce03..d4085379b4 100644
--- a/pylintrc
+++ b/pylintrc
@@ -110,7 +110,9 @@ generated-members=
get_url,
size,
content,
- status_code
+ status_code,
+# For factory_body factories
+ create
[BASIC]
diff --git a/rakefiles/jasmine.rake b/rakefiles/jasmine.rake
index 1e5050801e..4182bef9e2 100644
--- a/rakefiles/jasmine.rake
+++ b/rakefiles/jasmine.rake
@@ -48,6 +48,7 @@ def template_jasmine_runner(lib)
sh("node_modules/.bin/coffee -c #{coffee_files.join(' ')}")
end
phantom_jasmine_path = File.expand_path("node_modules/phantom-jasmine")
+ jasmine_reporters_path = File.expand_path("node_modules/jasmine-reporters")
common_js_root = File.expand_path("common/static/js")
common_coffee_root = File.expand_path("common/static/coffee/src")
@@ -58,6 +59,7 @@ def template_jasmine_runner(lib)
js_specs = Dir[spec_glob].sort_by {|p| [p.split('/').length, p]} .map {|f| File.expand_path(f)}
js_source = Dir[src_glob].sort_by {|p| [p.split('/').length, p]} .map {|f| File.expand_path(f)}
+ report_dir = report_dir_path("#{lib}/jasmine")
template = ERB.new(File.read("common/templates/jasmine/jasmine_test_runner.html.erb"))
template_output = "#{lib}/jasmine_test_runner.html"
File.open(template_output, 'w') do |f|
@@ -66,6 +68,11 @@ def template_jasmine_runner(lib)
yield File.expand_path(template_output)
end
+def run_phantom_js(url)
+ phantomjs = ENV['PHANTOMJS_PATH'] || 'phantomjs'
+ sh("#{phantomjs} node_modules/jasmine-reporters/test/phantomjs-testrunner.js #{url}")
+end
+
[:lms, :cms].each do |system|
desc "Open jasmine tests for #{system} in your default browser"
task "browse_jasmine_#{system}" => :assets do
@@ -78,14 +85,16 @@ end
desc "Use phantomjs to run jasmine tests for #{system} from the console"
task "phantomjs_jasmine_#{system}" => :assets do
- phantomjs = ENV['PHANTOMJS_PATH'] || 'phantomjs'
django_for_jasmine(system, false) do |jasmine_url|
- sh("#{phantomjs} node_modules/phantom-jasmine/lib/run_jasmine_test.coffee #{jasmine_url}")
+ run_phantom_js(jasmine_url)
end
end
end
-Dir["common/lib/*"].select{|lib| File.directory?(lib)}.each do |lib|
+STATIC_JASMINE_TESTS = Dir["common/lib/*"].select{|lib| File.directory?(lib)}
+STATIC_JASMINE_TESTS << 'common/static/coffee'
+
+STATIC_JASMINE_TESTS.each do |lib|
desc "Open jasmine tests for #{lib} in your default browser"
task "browse_jasmine_#{lib}" do
template_jasmine_runner(lib) do |f|
@@ -97,26 +106,14 @@ Dir["common/lib/*"].select{|lib| File.directory?(lib)}.each do |lib|
desc "Use phantomjs to run jasmine tests for #{lib} from the console"
task "phantomjs_jasmine_#{lib}" do
- phantomjs = ENV['PHANTOMJS_PATH'] || 'phantomjs'
template_jasmine_runner(lib) do |f|
- sh("#{phantomjs} node_modules/phantom-jasmine/lib/run_jasmine_test.coffee #{f}")
+ run_phantom_js(f)
end
end
end
desc "Open jasmine tests for discussion in your default browser"
-task "browse_jasmine_discussion" do
- template_jasmine_runner("common/static/coffee") do |f|
- sh("python -m webbrowser -t 'file://#{f}'")
- puts "Press ENTER to terminate".red
- $stdin.gets
- end
-end
+task "browse_jasmine_discussion" => "browse_jasmine_common/static/coffee"
desc "Use phantomjs to run jasmine tests for discussion from the console"
-task "phantomjs_jasmine_discussion" do
- phantomjs = ENV['PHANTOMJS_PATH'] || 'phantomjs'
- template_jasmine_runner("common/static/coffee") do |f|
- sh("#{phantomjs} node_modules/phantom-jasmine/lib/run_jasmine_test.coffee #{f}")
- end
-end
+task "phantomjs_jasmine_discussion" => "phantomjs_jasmine_common/static/coffee"
diff --git a/rakefiles/prereqs.rake b/rakefiles/prereqs.rake
index f453372065..ff8b4b8784 100644
--- a/rakefiles/prereqs.rake
+++ b/rakefiles/prereqs.rake
@@ -31,6 +31,7 @@ task :install_python_prereqs => "ws:migrate" do
unchanged = 'Python requirements unchanged, nothing to install'
when_changed(unchanged, ['requirements/**/*'], [site_packages_dir]) do
ENV['PIP_DOWNLOAD_CACHE'] ||= '.pip_download_cache'
+ sh('pip install --exists-action w -r requirements/edx/pre.txt')
sh('pip install --exists-action w -r requirements/edx/base.txt')
sh('pip install --exists-action w -r requirements/edx/post.txt')
# requirements/private.txt is used to install our libs as
diff --git a/rakefiles/tests.rake b/rakefiles/tests.rake
index ebe8ea6375..448a482f04 100644
--- a/rakefiles/tests.rake
+++ b/rakefiles/tests.rake
@@ -12,10 +12,11 @@ def run_under_coverage(cmd, root)
return cmd
end
-def run_tests(system, report_dir, stop_on_failure=true)
+def run_tests(system, report_dir, test_id=nil, stop_on_failure=true)
ENV['NOSE_XUNIT_FILE'] = File.join(report_dir, "nosetests.xml")
dirs = Dir["common/djangoapps/*"] + Dir["#{system}/djangoapps/*"]
- cmd = django_admin(system, :test, 'test', '--logging-clear-handlers', *dirs.each)
+ test_id = dirs.join(' ') if test_id.nil? or test_id == ''
+ cmd = django_admin(system, :test, 'test', '--logging-clear-handlers', test_id)
sh(run_under_coverage(cmd, system)) do |ok, res|
if !ok and stop_on_failure
abort "Test failed!"
@@ -25,6 +26,16 @@ def run_tests(system, report_dir, stop_on_failure=true)
end
def run_acceptance_tests(system, report_dir, harvest_args)
+ # HACK: Since now the CMS depends on the existence of some database tables
+ # that used to be in LMS (Role/Permissions for Forums) we need to make
+ # sure the acceptance tests create/migrate the database tables
+ # that are represented in the LMS. We might be able to address this by moving
+ # out the migrations from lms/django_comment_client, but then we'd have to
+ # repair all the existing migrations from the upgrade tables in the DB.
+ if system == :cms
+ sh(django_admin('lms', 'acceptance', 'syncdb', '--noinput'))
+ sh(django_admin('lms', 'acceptance', 'migrate', '--noinput'))
+ end
sh(django_admin(system, 'acceptance', 'syncdb', '--noinput'))
sh(django_admin(system, 'acceptance', 'migrate', '--noinput'))
sh(django_admin(system, 'acceptance', 'harvest', '--debug-mode', '--tag -skip', harvest_args))
@@ -44,13 +55,13 @@ TEST_TASK_DIRS = []
# Per System tasks
desc "Run all django tests on our djangoapps for the #{system}"
- task "test_#{system}", [:stop_on_failure] => ["clean_test_files", :predjango, "#{system}:gather_assets:test", "fasttest_#{system}"]
+ task "test_#{system}", [:test_id, :stop_on_failure] => ["clean_test_files", :predjango, "#{system}:gather_assets:test", "fasttest_#{system}"]
# Have a way to run the tests without running collectstatic -- useful when debugging without
# messing with static files.
- task "fasttest_#{system}", [:stop_on_failure] => [report_dir, :install_prereqs, :predjango] do |t, args|
- args.with_defaults(:stop_on_failure => 'true')
- run_tests(system, report_dir, args.stop_on_failure)
+ task "fasttest_#{system}", [:test_id, :stop_on_failure] => [report_dir, :install_prereqs, :predjango] do |t, args|
+ args.with_defaults(:stop_on_failure => 'true', :test_id => nil)
+ run_tests(system, report_dir, args.test_id, args.stop_on_failure)
end
# Run acceptance tests
@@ -100,7 +111,7 @@ end
task :test do
TEST_TASK_DIRS.each do |dir|
- Rake::Task["test_#{dir}"].invoke(false)
+ Rake::Task["test_#{dir}"].invoke(nil, false)
end
if $failed_tests > 0
diff --git a/requirements/edx/base.txt b/requirements/edx/base.txt
index 3d8b95f8e2..01768bcac9 100644
--- a/requirements/edx/base.txt
+++ b/requirements/edx/base.txt
@@ -29,7 +29,6 @@ mako==0.7.3
Markdown==2.2.1
networkx==1.7
nltk==2.0.4
-numpy==1.6.2
paramiko==1.9.0
path.py==3.0.1
Pillow==1.7.8
@@ -43,6 +42,7 @@ python-openid==2.2.5
pytz==2012h
PyYAML==3.10
requests==0.14.2
+scipy==0.11.0
Shapely==1.2.16
sorl-thumbnail==11.12
South==0.7.6
@@ -71,7 +71,7 @@ transifex-client==0.8
coverage==3.6
factory_boy==2.0.2
lettuce==0.2.16
-mock==0.8.0
+mock==1.0.1
nosexcover==1.0.7
pep8==1.4.5
pylint==0.28
@@ -82,3 +82,5 @@ django_nose==1.1
django-jasmine==0.3.2
django_debug_toolbar
django-debug-toolbar-mongo
+
+git+https://github.com/mfogel/django-settings-context-processor.git
diff --git a/requirements/edx/github.txt b/requirements/edx/github.txt
index 6b28d3edd9..f280d66557 100644
--- a/requirements/edx/github.txt
+++ b/requirements/edx/github.txt
@@ -9,4 +9,4 @@
# Our libraries:
-e git+https://github.com/edx/XBlock.git@2144a25d#egg=XBlock
--e git+https://github.com/edx/codejail.git@07494f1#egg=codejail
+-e git+https://github.com/edx/codejail.git@72cf791#egg=codejail
diff --git a/requirements/edx/post.txt b/requirements/edx/post.txt
index e1e26b381a..b637b65db0 100644
--- a/requirements/edx/post.txt
+++ b/requirements/edx/post.txt
@@ -1,6 +1,2 @@
-
-# This must be installed after distribute 0.6.28
-MySQL-python==1.2.4c1
-
-# This must be installed after numpy
-scipy==0.11.0
+# This must be installed after distribute has been updated.
+MySQL-python==1.2.4
diff --git a/requirements/edx/pre.txt b/requirements/edx/pre.txt
new file mode 100644
index 0000000000..a8dff9bf9a
--- /dev/null
+++ b/requirements/edx/pre.txt
@@ -0,0 +1,3 @@
+# Numpy and scipy can't be installed in the same pip run.
+# Install numpy before other things to help resolve the problem.
+numpy==1.6.2